Integration

Android Integration

Add Reqflow to your Android app using WebView for seamless feedback collection.

Prerequisites

  • Android API 21+Minimum deployment target (Lollipop)
  • Android StudioArctic Fox or later recommended
  • Reqflow appCreated in the dashboard

Add Internet Permission

Add the internet permission to your AndroidManifest.xml:

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- ... -->
</manifest>

Basic Implementation

Create a new Activity for the feedback screen:

FeedbackActivity.kt
import android.os.Bundle
import android.provider.Settings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import java.net.URLEncoder

class FeedbackActivity : AppCompatActivity() {

    // Your app's slug from the Reqflow dashboard
    private val appId = "your-app-id"

    private lateinit var webView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        webView = WebView(this).apply {
            settings.javaScriptEnabled = true
            settings.domStorageEnabled = true
            webViewClient = WebViewClient()
        }

        setContentView(webView)
        supportActionBar?.title = "Feedback"

        loadFeedbackPage()
    }

    private fun loadFeedbackPage() {
        // Get user info from your auth system
        val userId = getUserId()
        val userName = getUserName()
        val userEmail = getUserEmail()

        val url = buildString {
            append("https://reqflow.com/vote/$appId")
            append("?user_id=${URLEncoder.encode(userId, "UTF-8")}")
            append("&user_name=${URLEncoder.encode(userName, "UTF-8")}")
            userEmail?.let {
                append("&user_email=${URLEncoder.encode(it, "UTF-8")}")
            }
        }

        webView.loadUrl(url)
    }

    private fun getUserId(): String {
        // Option 1: Use Android ID for anonymous users
        return Settings.Secure.getString(
            contentResolver,
            Settings.Secure.ANDROID_ID
        )

        // Option 2: Use your auth system's user ID
        // return AuthManager.currentUser?.id ?: UUID.randomUUID().toString()
    }

    private fun getUserName(): String {
        // Return the user's display name from your auth system
        // return AuthManager.currentUser?.name ?: "Anonymous"
        return "User"
    }

    private fun getUserEmail(): String? {
        // Optional: Return user's email if available
        // return AuthManager.currentUser?.email
        return null
    }

    override fun onBackPressed() {
        if (webView.canGoBack()) {
            webView.goBack()
        } else {
            super.onBackPressed()
        }
    }
}

Jetpack Compose Implementation

If you're using Jetpack Compose, use the Accompanist WebView library:

build.gradle
// Add to build.gradle
dependencies {
    implementation "com.google.accompanist:accompanist-webview:0.30.1"
}
FeedbackScreen.kt
import android.provider.Settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.google.accompanist.web.WebView
import com.google.accompanist.web.rememberWebViewState
import java.net.URLEncoder

@Composable
fun FeedbackScreen(
    appId: String,
    userId: String,
    userName: String,
    userEmail: String? = null
) {
    val url = buildString {
        append("https://reqflow.com/vote/$appId")
        append("?user_id=${URLEncoder.encode(userId, "UTF-8")}")
        append("&user_name=${URLEncoder.encode(userName, "UTF-8")}")
        userEmail?.let {
            append("&user_email=${URLEncoder.encode(it, "UTF-8")}")
        }
    }

    val state = rememberWebViewState(url)

    WebView(
        state = state,
        onCreated = { webView ->
            webView.settings.javaScriptEnabled = true
        }
    )
}

// Usage
@Composable
fun SettingsScreen() {
    val context = LocalContext.current
    val androidId = Settings.Secure.getString(
        context.contentResolver,
        Settings.Secure.ANDROID_ID
    )

    FeedbackScreen(
        appId = "your-app-id",
        userId = androidId,
        userName = "John Doe",
        userEmail = "john@example.com"
    )
}

Opening Feedback from a Button

Add a button in your app to open the feedback screen:

// In your Activity or Fragment
binding.feedbackButton.setOnClickListener {
    val intent = Intent(this, FeedbackActivity::class.java)
    startActivity(intent)
}

// Or using Navigation Component
findNavController().navigate(R.id.feedbackFragment)

Consistent User ID

Use ANDROID_ID or your auth system's user ID. Don't generate a new UUID each time, or users will lose their votes.

Secure Authentication (Optional)

For enhanced security, you can add HMAC signature verification to prevent user impersonation. The signature should be generated by your backend and passed to the app.

SignatureHelper.kt
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

/**
 * Generate HMAC signature for secure authentication (optional)
 */
object SignatureHelper {
    fun generateSignature(userId: String, secret: String): Pair<String, String> {
        val timestamp = (System.currentTimeMillis() / 1000).toString()
        val payload = "$userId:$timestamp"

        val mac = Mac.getInstance("HmacSHA256")
        mac.init(SecretKeySpec(secret.toByteArray(Charsets.UTF_8), "HmacSHA256"))

        val signature = mac.doFinal(payload.toByteArray(Charsets.UTF_8))
            .joinToString("") { "%02x".format(it) }

        return Pair(signature, timestamp)
    }
}

// Usage
fun loadFeedbackPageWithSignature() {
    val userId = getUserId()
    val secret = getSecretFromBackend() // Fetch from your backend

    val (signature, timestamp) = SignatureHelper.generateSignature(userId, secret)

    val url = buildString {
        append("https://reqflow.com/vote/$appId")
        append("?user_id=${URLEncoder.encode(userId, "UTF-8")}")
        append("&user_name=${URLEncoder.encode(getUserName(), "UTF-8")}")
        append("&signature=${URLEncoder.encode(signature, "UTF-8")}")
        append("&timestamp=$timestamp")
    }

    webView.loadUrl(url)
}

Security Note

Don't hardcode the secret in your app. Fetch it from your backend or use Android Keystore for secure storage. The signature feature is optional and only needed if you enable it on your Reqflow server.

Best Practices

  • Handle Back ButtonOverride onBackPressed to handle WebView navigation history
  • Enable JavaScriptMake sure javaScriptEnabled is set to true
  • Dark ModeReqflow automatically adapts to the system theme
  • Loading StateConsider adding a ProgressBar while the WebView loads