Petnow LogoPetnow
Android SDK

UI Guide

Using Petnow Android SDK UI components and customizing them.


Overview

The Petnow UI module for Android provides developers with ready-to-use UI components for capturing pet biometric data. This guide explains how to integrate and use these components in your Android application, primarily focusing on the PetnowCameraFragment and its associated detection capabilities.

Key Components

PetnowCameraFragment

A fragment for capturing pet biometric data using the device camera. This fragment manages camera initialization, pet detection (dog nose or cat face), and provides visual feedback during the capture process.

PetnowCameraDetectionListener

Interface for receiving callbacks during the pet detection process.

Important Implementation Notes

  • The fragment automatically handles camera permission requests
  • For dogs, nose detection is prioritized and visualized
  • For cats, face detection is prioritized and visualized
  • Detection progress is shown through visual indicators
  • The fragment will automatically exit if PetnowApiClient is not properly initialized
  • You must call PetnowApiClient.configureDetectionMode() before creating or navigating to your fragment

Prerequisites

  • PetnowApiClient must be initialized with a valid API key
  • PetnowApiClient.configureDetectionMode() must be called before creating or navigating to your fragment
  • Camera permissions are automatically requested by the framework

Using PetnowCameraFragment

Basic Implementation

class CustomCameraFragment : PetnowCameraFragment(), PetnowCameraDetectionListener {

    override fun onAttach(context: Context) {
        super.onAttach(context)
        setPetnowCameraDetectionListener(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Add custom layout overlay
        addCustomLayout(R.layout.fragment_custom_camera)
    }

    override fun onAddedCustomLayout(view: View) {
        super.onAddedCustomLayout(view)

        // Initialize custom UI elements
        val statusText = view.findViewById<TextView>(R.id.status_text)
        val progressBar = view.findViewById<ProgressBar>(R.id.progress_bar)
        val captureButton = view.findViewById<Button>(R.id.capture_button)

        statusText.text = "Position your pet in the camera frame"
        captureButton.setOnClickListener {
            resumeDetection()
            captureButton.isEnabled = false
        }
    }

    override fun onDetectionStatus(primaryDetectionStatus: PetnowDetectionStatus) {
        val statusText = view?.findViewById<TextView>(R.id.status_text)
        val captureButton = view?.findViewById<Button>(R.id.capture_button)

        when (primaryDetectionStatus) {
            PetnowDetectionStatus.Detected -> {
                statusText?.text = "Perfect! Biometric data captured successfully"
                captureButton?.isEnabled = true
            }
            PetnowDetectionStatus.NoObject -> {
                statusText?.text = "No pet detected. Please position your pet in frame"
                captureButton?.isEnabled = false
            }
            PetnowDetectionStatus.TooClose -> {
                statusText?.text = "Too close! Please move camera back"
                captureButton?.isEnabled = false
            }
            PetnowDetectionStatus.TooFarAway -> {
                statusText?.text = "Too far! Please move camera closer"
                captureButton?.isEnabled = false
            }
            else -> {
                statusText?.text = "Position your pet for best results"
                captureButton?.isEnabled = false
            }
        }
    }

    override fun onDetectionProgress(progress: Int) {
        val progressBar = view?.findViewById<ProgressBar>(R.id.progress_bar)
        val statusText = view?.findViewById<TextView>(R.id.status_text)

        progressBar?.progress = progress

        if (progress >= 100) {
            statusText?.text = "Detection complete!"
        }
    }

    override fun onDetectionFinished(result: DetectionCaptureResult) {
        when (result) {
            is DetectionCaptureResult.Success -> {
                Log.d("CustomCameraFragment",
                    "Captured ${result.noseImageFiles.size} fingerprints and ${result.faceImageFiles.size} appearances")

                // Process captured images - upload to server
                uploadImages(result.noseImageFiles, result.faceImageFiles)

                // Navigate back or show success message
                parentFragmentManager.popBackStack()
            }
            is DetectionCaptureResult.Fail -> {
                Log.e("CustomCameraFragment", "Detection failed")
                // Show error message to user
            }
        }
    }

    private fun uploadImages(noseImages: List<File>, faceImages: List<File>) {
        // Implementation example - upload images using PetnowApiClient
        // This would typically be handled by your ViewModel or Repository
    }
}

Detection Status Reference

Here's a comprehensive reference of all detection statuses and their meanings:

Basic Detection Statuses

StatusDescription
PetnowDetectionStatus.DetectedSuccessfully detected the pet's biometric feature
PetnowDetectionStatus.NoObjectNo pet detected in the frame

Distance Issues

StatusDescription
PetnowDetectionStatus.TooCloseCamera is too close to the pet
PetnowDetectionStatus.TooFarAwayCamera is too far from the pet

Lighting Issues

StatusDescription
PetnowDetectionStatus.TooBrightToo much light in the environment
PetnowDetectionStatus.TooDarkNot enough light in the environment
PetnowDetectionStatus.ShadowDetectedShadows detected on the pet's nose/face
PetnowDetectionStatus.GlareDetectedGlare detected on the pet's nose/face

Clarity Issues

StatusDescription
PetnowDetectionStatus.TooBlurredImage is too blurry for detection
PetnowDetectionStatus.MotionBlurDetectedMotion blur detected
PetnowDetectionStatus.DefocusedBlurDetectedCamera is out of focus

Angle Issues

StatusDescription
PetnowDetectionStatus.NotFrontFacePet's face is not directly facing the camera
PetnowDetectionStatus.NotFrontNosePet's nose is not directly facing the camera

Obstruction Issues

StatusDescription
PetnowDetectionStatus.NoseNotFoundDog's nose not detected in the frame
PetnowDetectionStatus.FurDetectedFur is obstructing the detection

Authenticity Issues

StatusDescription
PetnowDetectionStatus.FakeDetectedSpoof detected (e.g., photo, video, or screen); not a real pet face

Detection Capture Result

sealed class DetectionCaptureResult {
    data class Success(
        val noseImageFiles: List<File>,
        val faceImageFiles: List<File>
    ) : DetectionCaptureResult()

    data object Fail : DetectionCaptureResult()
}

Success Variant

Contains the successfully captured image files:

  • noseImageFiles: List of File objects containing dog nose images
  • faceImageFiles: List of File objects containing pet face images

Fail Variant

Indicates that the detection process failed to capture usable images.

Custom Layout Implementation

Create Custom Layout XML

<!-- fragment_custom_camera.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Status Text -->
    <TextView
        android:id="@+id/status_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Center your pet in the frame"
        android:textColor="#FFFFFF"
        android:textSize="18sp"
        android:padding="12dp"
        android:background="#33000000"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="48dp" />

    <!-- Resume Detection Button -->
    <Button
        android:id="@+id/capture_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Resume Detection"
        android:backgroundTint="#FF5722"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginBottom="48dp"
        android:enabled="false" />

    <!-- Progress Indicator -->
    <ProgressBar
        android:id="@+id/progress_bar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="4dp"
        android:progressTint="#4CAF50"
        app:layout_constraintTop_toBottomOf="@id/status_text"
        app:layout_constraintStart_toStartOf="@id/status_text"
        app:layout_constraintEnd_toEndOf="@id/status_text"
        android:layout_marginTop="4dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

Using FragmentManager

// Configure detection mode first
PetnowApiClient.configureDetectionMode(
    purpose = DetectionPurpose.PET_PROFILE_REGISTRATION,
    species = PetSpecies.DOG
)

// Create and navigate to custom camera fragment
val cameraFragment = CustomCameraFragment.newInstance(
    species = PetSpecies.DOG,
    purpose = DetectionPurpose.PET_PROFILE_REGISTRATION
) { noseImages, faceImages ->
    // Handle captured images
    uploadImages(noseImages, faceImages)
}

supportFragmentManager.beginTransaction()
    .replace(R.id.fragment_container, cameraFragment)
    .addToBackStack(null)
    .commit()

Using Navigation Component

// In your nav_graph.xml
<fragment
    android:id="@+id/cameraFragment"
    android:name="com.example.CustomCameraFragment"
    android:label="Camera Fragment">
    <argument
        android:name="species"
        app:argType="com.petnow.PetSpecies" />
    <argument
        android:name="purpose"
        app:argType="com.petnow.DetectionPurpose" />
</fragment>

Error Handling

Permission Handling

override fun onDetectionStatus(primaryDetectionStatus: PetnowDetectionStatus) {
    when (primaryDetectionStatus) {
        PetnowDetectionStatus.Error -> {
            // Check if it's a permission error
            when (val status = ContextCompat.checkSelfPermission(
                requireContext(),
                Manifest.permission.CAMERA
            )) {
                PackageManager.PERMISSION_DENIED -> {
                    // Request permission
                    requestCameraPermission()
                }
                PackageManager.PERMISSION_GRANTED -> {
                    // Permission granted but still error - other issue
                    showError("Camera error occurred")
                }
            }
        }
        // Handle other statuses...
    }
}

private fun requestCameraPermission() {
    ActivityCompat.requestPermissions(
        requireActivity(),
        arrayOf(Manifest.permission.CAMERA),
        CAMERA_PERMISSION_REQUEST_CODE
    )
}

Lifecycle Management

override fun onResume() {
    super.onResume()
    // Resume detection when fragment becomes visible
    resumeDetection()
}

override fun onPause() {
    super.onPause()
    // Pause detection when fragment is not visible
    // Note: PetnowCameraFragment handles this automatically
}

override fun onDestroyView() {
    super.onDestroyView()
    // Clean up resources
}

Best Practices

  1. Configure Before Navigation: Always call configureDetectionMode() before creating camera fragment
  2. Handle Permissions: Implement proper camera permission handling
  3. Provide User Feedback: Show clear status messages and progress indicators
  4. Error Recovery: Implement retry mechanisms for failed detections
  5. Memory Management: Clean up fragment resources properly
  6. Testing: Test with various lighting conditions and pet positions
  7. User Experience: Provide guidance for optimal pet positioning

Performance Considerations

  • Camera preview resolution affects performance - balance quality and performance
  • Detection processing can be resource intensive - monitor device temperature
  • Use background threads for image processing and uploads
  • Implement proper memory management to avoid leaks

Support

If you encounter any issues or have questions about the Petnow Android UI components, please contact Petnow support at support@petnow.io.