Skip to main content

Development, Testing, and Sample App

This guide covers development best practices, testing strategies, and information about the Entrupy Android SDK sample application.

Development Environment Setup

Prerequisites

  • Android Studio: Latest stable version (recommended: Android Studio Hedgehog or newer)
  • Android SDK: API level 24+ (Android 7.0 Nougat)
  • Gradle: Version 7.0+ (recommended: 8.0+)
  • Kotlin: Version 1.5+ (recommended: 1.9+)
  • Java: Version 11+

Project Configuration

1. Add Repository

Ensure Maven Central is included in your project's repository configuration:

// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/entrupy/entrupy-sdk-android")
credentials {
username = System.getenv("GITHUB_USER")
password = System.getenv("GITHUB_TOKEN")
}
}
}
}

2. Add Dependencies

// app/build.gradle.kts
dependencies {
implementation("com.entrupy:sdk:1.0.0")

// Optional: For testing
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

3. Configure Permissions

The SDK automatically handles permission requests, but ensure your app's manifest includes:

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

<uses-feature android:name="android.hardware.camera.any" />

Testing Strategies

1. Unit Testing

Test your SDK integration logic independently:

import com.entrupy.sdk.app.EntrupyApp
import com.entrupy.sdk.model.ConfigMetadata
import com.entrupy.sdk.model.ProductCategory

// Example unit test
class EntrupyAppTest {

@Test
fun testUserAuthorization() {
// Mock SDK responses
val mockApp = mock<EntrupyApp>()
whenever(mockApp.generateSDKAuthorizationRequest()).thenReturn("test_request")

// Test authorization flow
val authRequest = mockApp.generateSDKAuthorizationRequest()
assertEquals("test_request", authRequest)
}

@Test
fun testConfigMetadataValidation() {
val validMetadata = ConfigMetadata(
brandId = "nike",
productCategory = ProductCategory.Sneakers,
customerItemId = "test_123"
)

assertTrue(isValidMetadata(validMetadata))

// Test with minimal required fields
val minimalMetadata = ConfigMetadata(
brandId = "gucci",
productCategory = ProductCategory.Luxury
)

assertTrue(isValidMetadata(minimalMetadata))
}
}

2. Integration Testing

Test the complete flow with your backend:

import com.entrupy.sdk.app.EntrupyApp
import com.entrupy.sdk.listeners.SdkLoginCallback

// Example integration test
@RunWith(AndroidJUnit4::class)
class EntrupyIntegrationTest {

@Test
fun testCompleteAuthenticationFlow() {
// 1. Initialize SDK
EntrupyApp.init(ApplicationProvider.getApplicationContext())

// 2. Authorize user
val entrupyApp = EntrupyApp.sharedInstance()
val authRequest = entrupyApp.generateSDKAuthorizationRequest()
val signedRequest = callBackendForAuthorization(authRequest)

// 3. Login to SDK
var loginSuccess = false
entrupyApp.loginUser(signedRequest, object : SdkLoginCallback {
override fun onLoginStarted() {
Log.d("Test", "Login started")
}

override fun onLoginSuccess(expirationTime: Long) {
loginSuccess = true
}

override fun onLoginError(
errorCode: Int,
description: String,
localizedDescription: String
) {
fail("Login failed: $description")
}
})

// 4. Verify authorization
assertTrue(entrupyApp.isAuthorizationValid())

// 5. Test capture flow (if needed)
// Note: This would require a real device or emulator with camera
}
}

3. UI Testing

Test the user interface components:

@RunWith(AndroidJUnit4::class)
class EntrupyUITest {

@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)

@Test
fun testCaptureFlowLaunch() {
// Navigate to capture button
onView(withId(R.id.btn_start_capture))
.perform(click())

// Verify capture flow is launched
// Note: This would require custom assertions based on your UI
}

@Test
fun testPermissionHandling() {
// Test permission denial flow
// This requires special setup to simulate permission denial
}
}

4. Mock Backend for Testing

Create a mock backend for development and testing:

// Mock backend implementation
class MockEntrupyBackend {

fun authorizeUser(sdkRequest: String): String {
// Return a mock signed request
return "mock_signed_request_${System.currentTimeMillis()}"
}

fun getItemStatus(customerItemId: String): ItemStatus {
return ItemStatus(
customerItemId = customerItemId,
status = "completed",
result = mapOf("authenticity" to "authentic"),
eta = null
)
}
}

Sample Application

Entrupy provides a comprehensive sample Android application to help developers understand how to integrate the Entrupy SDK into their own projects.

📱 Sample Project Repository: github.com/entrupy/entrupy-sdk-sampleproject-android

Understanding the Integration via Git History

The sample project is designed to help you understand exactly what changes are required to integrate the Entrupy SDK into your Android application. Navigate to the "Entrupy SDK Integration" commit in the repository's Git history to see a focused diff containing only the changes necessary for SDK integration—without any unrelated project setup or boilerplate code.

This approach allows you to:

  • See the exact files that need to be modified
  • Understand the minimal code changes required
  • Copy relevant snippets directly into your own project
  • Compare your implementation against a working reference

Features Demonstrated

  1. Complete Integration Flow

    • SDK initialization
    • User authorization
    • Capture flow integration
    • Result handling
  2. Best Practices

    • Error handling
    • Session management
    • UI/UX patterns
    • Backend integration
  3. Advanced Features

    • Custom UI theming
    • Multi-language support
    • Offline handling
    • Analytics integration

Running the Sample App

  1. Clone the Repository

    git clone https://github.com/entrupy/entrupy-sdk-sampleproject-android.git
    cd entrupy-sdk-sampleproject-android
  2. Configure GitHub Packages Authentication

    Add your GitHub credentials to ~/.gradle/gradle.properties:

    gpr.user=YOUR_GITHUB_USERNAME
    gpr.token=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN
  3. Configure Sample App

    • Update the backend URL configuration as needed
    • Add your Entrupy credentials to local.properties
  4. Run the App

    ./gradlew assembleDebug

Key Sample App Components

EntrupyManager.kt

import com.entrupy.sdk.app.EntrupyApp
import com.entrupy.sdk.listeners.SdkLoginCallback
import com.entrupy.sdk.listeners.CaptureCallback
import com.entrupy.sdk.model.ConfigMetadata

class EntrupyManager private constructor() {

companion object {
@Volatile
private var INSTANCE: EntrupyManager? = null

fun getInstance(): EntrupyManager {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: EntrupyManager().also { INSTANCE = it }
}
}
}

private val entrupyApp: EntrupyApp by lazy { EntrupyApp.sharedInstance() }

fun initialize(application: Application) {
EntrupyApp.init(application)
}

fun authorizeUser(signedRequest: String, callback: (Boolean) -> Unit) {
entrupyApp.loginUser(signedRequest, object : SdkLoginCallback {
override fun onLoginStarted() {
// Show loading
}

override fun onLoginSuccess(expirationTime: Long) {
callback(true)
}

override fun onLoginError(
errorCode: Int,
description: String,
localizedDescription: String
) {
callback(false)
}
})
}

fun startCapture(metadata: ConfigMetadata) {
entrupyApp.startCapture(metadata)
}
}

SessionManager.kt

import com.entrupy.sdk.app.EntrupyApp

class SessionManager(context: Context) {

private val sharedPrefs = context.getSharedPreferences("entrupy_session", Context.MODE_PRIVATE)

fun saveTokenExpiry(expiryTimestamp: Long) {
sharedPrefs.edit()
.putLong("token_expiry", expiryTimestamp)
.apply()
}

fun isTokenValid(): Boolean {
val expiry = sharedPrefs.getLong("token_expiry", 0L)
return System.currentTimeMillis() < (expiry - 60_000) // 1-minute buffer
}

fun clearSession() {
sharedPrefs.edit().clear().apply()
EntrupyApp.sharedInstance().cleanup()
}
}

Development Best Practices

1. Error Handling

import com.entrupy.sdk.listeners.EntrupyErrorCode

// Centralized error handling
object ErrorHandler {

fun handleSDKError(errorCode: Int, description: String) {
when (errorCode) {
EntrupyErrorCode.UNAUTHORIZED_ACCESS -> {
// Re-authorize user
reauthorizeUser()
}
EntrupyErrorCode.TOO_MANY_REQUESTS -> {
// Handle rate limiting
handleRateLimit()
}
EntrupyErrorCode.SERVICE_UNAVAILABLE -> {
// Show service unavailable error
showServiceUnavailable()
}
else -> {
// Log and show generic error
Log.e("Entrupy", "Error: $errorCode - $description")
showGenericError()
}
}
}
}

2. Logging

// Structured logging
object EntrupyLogger {

private const val TAG = "EntrupyApp"

fun logEvent(event: String, properties: Map<String, Any> = emptyMap()) {
Log.d(TAG, "Event: $event, Properties: $properties")
// Send to analytics service
}

fun logError(error: Throwable, context: String = "") {
Log.e(TAG, "Error in $context", error)
// Send to crash reporting service
}
}

3. Configuration Management

// Environment-specific configuration
object Config {

const val API_BASE_URL = "https://api.entrupy.com"
const val WEBHOOK_URL = "https://your-backend.com/webhooks"

// Environment-specific settings
object Environment {
const val PRODUCTION = "production"
const val STAGING = "staging"
const val DEVELOPMENT = "development"
}

val currentEnvironment = if (BuildConfig.DEBUG) {
Environment.DEVELOPMENT
} else {
Environment.PRODUCTION
}
}

Debugging Tips

1. Enable Debug Logging

// Enable detailed logging in debug builds
if (BuildConfig.DEBUG) {
// SDK logging is enabled by default in debug builds
Log.d("Entrupy", "Debug mode enabled")
}

2. Network Inspection

Use tools like Charles Proxy or Flipper to inspect network requests:

// Add network interceptor for debugging
OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request()
Log.d("Network", "Request: ${request.url}")
val response = chain.proceed(request)
Log.d("Network", "Response: ${response.code}")
response
}
.build()

3. UI Inspection

Use Android Studio's Layout Inspector to debug UI issues:

// Add debug information to views
if (BuildConfig.DEBUG) {
view.setTag(R.id.debug_tag, "entrupy_capture_button")
}