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
-
Complete Integration Flow
- SDK initialization
- User authorization
- Capture flow integration
- Result handling
-
Best Practices
- Error handling
- Session management
- UI/UX patterns
- Backend integration
-
Advanced Features
- Custom UI theming
- Multi-language support
- Offline handling
- Analytics integration
Running the Sample App
-
Clone the Repository
git clone https://github.com/entrupy/entrupy-sdk-sampleproject-android.git
cd entrupy-sdk-sampleproject-android -
Configure GitHub Packages Authentication
Add your GitHub credentials to
~/.gradle/gradle.properties:gpr.user=YOUR_GITHUB_USERNAME
gpr.token=YOUR_GITHUB_PERSONAL_ACCESS_TOKEN -
Configure Sample App
- Update the backend URL configuration as needed
- Add your Entrupy credentials to
local.properties
-
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")
}