Android Integration
The Android integration is a WebView-based implementation that loads Hubble’s gift card store. This guide shows you how to integrate Hubble into your Android app using a standard WebView.
Basic Setup
At its core, integrating Hubble is straightforward - it’s a WebView loading a URL with your credentials:
val webView = WebView(context).apply {    settings.javaScriptEnabled = true}
val clientId = "your_client_id" // provided by Hubble teamval clientSecret = "your_client_secret" // provided by Hubble teamval token = "your_user_token" // unique identifier for your userval env = "dev" // "dev" or "prod"
val baseUrl = if (env == "prod") "vouchers.myhubble.money" else "vouchers.dev.myhubble.money"
webView.loadUrl("https://$baseUrl/classic?clientId=$clientId&clientSecret=$clientSecret&token=$token&wrap-plt=an")This basic setup will display the Hubble store. For a production-ready integration, you’ll want to add the following features:
Additional Setup
1. Back Navigation
Handle back button presses to navigate within the WebView:
fun goBack(): Boolean {    return if (webView.canGoBack()) {        webView.goBack()        true    } else {        false    }}
// In your Activityoverride fun onBackPressed() {    if (!goBack()) {        super.onBackPressed()    }}2. Analytics Events
Receive analytics events from the WebView using JavaScript interface:
import android.util.Logimport android.webkit.JavascriptInterfaceimport org.json.JSONExceptionimport org.json.JSONObject
class HubbleJavaScriptInterface {    @JavascriptInterface    fun onAnalyticsEvent(eventName: String, properties: String?) {        val propertiesMap = if (properties != null) {            parseJsonProperties(properties)        } else {            null        }
        // Send to your analytics provider        Log.d("HubbleAnalytics", "Event: $eventName, Properties: $propertiesMap")    }
    private fun parseJsonProperties(properties: String): Map<String, Any> {        return try {            val jsonObject = JSONObject(properties)            val map: MutableMap<String, Any> = HashMap()            val iterator = jsonObject.keys()            while (iterator.hasNext()) {                val key = iterator.next()                map[key] = jsonObject[key]            }            map        } catch (e: JSONException) {            Log.e("HubbleAnalytics", "Failed to parse: ${e.message}")            emptyMap()        }    }}
// Add to WebViewwebView.addJavascriptInterface(HubbleJavaScriptInterface(), "AndroidHost")3. URL Handling
Control which URLs open within the WebView vs external browser:
import android.content.Intentimport android.webkit.WebViewClientimport android.webkit.WebResourceRequest
class HubbleWebViewClient : WebViewClient() {    private val baseUrl = "vouchers.dev.myhubble.money" // or "vouchers.myhubble.money" for prod
    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {        val url = request?.url?.toString()
        return if (url?.contains(baseUrl) == true) {            false // Allow navigation within Hubble        } else {            // Open external URLs in browser            try {                val intent = Intent(Intent.ACTION_VIEW, request?.url)                view?.context?.startActivity(intent)                true            } catch (e: Exception) {                false            }        }    }}
// Set the WebViewClientwebView.webViewClient = HubbleWebViewClient()4. Additional WebView Settings
For optimal performance, configure these settings:
webView.apply {    settings.javaScriptEnabled = true    settings.javaScriptCanOpenWindowsAutomatically = true    settings.databaseEnabled = true    settings.domStorageEnabled = true    settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW    settings.setSupportZoom(false)    settings.builtInZoomControls = false    settings.displayZoomControls = false}Complete Example
Here’s a complete fragment implementation combining all the above features:
import android.content.Contextimport android.content.Intentimport android.os.Bundleimport android.util.Logimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport android.webkit.JavascriptInterfaceimport android.webkit.WebResourceRequestimport android.webkit.WebSettingsimport android.webkit.WebViewimport android.webkit.WebViewClientimport androidx.fragment.app.Fragmentimport org.json.JSONExceptionimport org.json.JSONObject
class HubbleWebViewFragment : Fragment() {
    private lateinit var webView: WebView    private val clientId = "your_client_id"    private val clientSecret = "your_client_secret"    private val token = "your_user_token"    private val env = "dev" // "dev" or "prod"
    private val baseUrl: String        get() = if (env == "prod") "vouchers.myhubble.money" else "vouchers.dev.myhubble.money"
    override fun onCreateView(        inflater: LayoutInflater,        container: ViewGroup?,        savedInstanceState: Bundle?    ): View? {        webView = WebView(requireContext()).apply {            id = View.generateViewId()
            settings.apply {                javaScriptEnabled = true                javaScriptCanOpenWindowsAutomatically = true                databaseEnabled = true                domStorageEnabled = true                mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW                setSupportZoom(false)                builtInZoomControls = false                displayZoomControls = false            }
            addJavascriptInterface(HubbleJavaScriptInterface(this@HubbleWebViewFragment), "AndroidHost")            webViewClient = HubbleWebViewClient(baseUrl, requireContext())
            val url = "https://$baseUrl/classic?clientId=$clientId&clientSecret=$clientSecret&token=$token&wrap-plt=an"            loadUrl(url)        }
        return webView    }
    fun goBack(): Boolean {        return if (webView.canGoBack()) {            webView.goBack()            true        } else {            false        }    }
    fun reload() {        webView.reload()    }}
class HubbleJavaScriptInterface(private val fragment: HubbleWebViewFragment) {    @JavascriptInterface    fun close() {        fragment.activity?.finish()    }
    @JavascriptInterface    fun reload() {        fragment.reload()    }
    @JavascriptInterface    fun onAnalyticsEvent(eventName: String, properties: String?) {        val propertiesMap = properties?.let { parseJsonProperties(it) }        Log.d("HubbleAnalytics", "Event: $eventName, Properties: $propertiesMap")        // Send to your analytics provider here    }
    private fun parseJsonProperties(properties: String): Map<String, Any> {        return try {            val jsonObject = JSONObject(properties)            val map: MutableMap<String, Any> = HashMap()            jsonObject.keys().forEach { key ->                map[key] = jsonObject[key]            }            map        } catch (e: JSONException) {            Log.e("HubbleAnalytics", "Failed to parse: ${e.message}")            emptyMap()        }    }}
class HubbleWebViewClient(    private val baseUrl: String,    private val context: Context) : WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {        val url = request?.url?.toString()
        return if (url?.contains(baseUrl) == true) {            false // Allow navigation within Hubble        } else {            try {                val intent = Intent(Intent.ACTION_VIEW, request?.url)                context.startActivity(intent)                true            } catch (e: Exception) {                false            }        }    }}Example Usage in Activity
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
    private lateinit var hubbleFragment: HubbleWebViewFragment
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)
        hubbleFragment = HubbleWebViewFragment()
        supportFragmentManager.beginTransaction()            .replace(R.id.fragment_container, hubbleFragment)            .commit()    }
    override fun onBackPressed() {        if (!hubbleFragment.goBack()) {            super.onBackPressed()        }    }}Configuration Options
You can customize the integration by modifying the URL parameters:
- Environment: Set envto"prod"for production or"dev"for development
- Initial Page: Add &page=<page_name>to open a specific page
For detailed information about supported pages and parameters, refer to our Deeplinks documentation.
Legacy: Android SDK (Deprecated)
⚠️ Deprecation Notice: The Hubble Android SDK is deprecated and no longer recommended for new integrations. Please use the WebView integration documented above. Existing integrations using the SDK will continue to work, but we recommend migrating to the WebView approach for better control and maintainability.
The Android SDK was a lightweight Android library around our web application. If you’re currently using it, here’s the reference documentation:
Setup
- Add Jitpack repository to your project’s Gradle file:
dependencyResolutionManagement {    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)    repositories {        google()        mavenCentral()        maven { url = uri("https://jitpack.io") }    }}- Add the dependency:
dependencies {    implementation ("money.myhubble:storesdk:0.0.3")}- Initialize the SDK:
Hubble.init(    env="dev", // If not given, will default to 'prod'    token="GOpBKIQ0xj", // Some token or ID that uniquely identifies your user    clientId= "clientA", // will be provided by Hubble team    clientSecret= "clientSecretA", // will be provided by Hubble team)- Open the store:
Hubble.open(context = context)SDK Features
Fragments:
hubbleFragment = Hubble.getFragment()hubbleFragment.goBack() // returns true/falseEvents:
Hubble.init(    ...    onAnalyticsEvent = { eventName: String, props: Map<String, Any> ->        Log.i(tag, "Received event $eventName from Hubble SDK. ${props}")    })Deeplinks:
Hubble.init(    ...    page = HubblePage(        page = "brand",        params = mapOf("brandId" to "uber")    ))For migration assistance, please contact the Hubble team.
 
 