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
PetnowApiClientis not properly initialized - You must call
PetnowApiClient.configureDetectionMode()before creating or navigating to your fragment
Prerequisites
PetnowApiClientmust be initialized with a valid API keyPetnowApiClient.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
| Status | Description |
|---|---|
PetnowDetectionStatus.Detected | Successfully detected the pet's biometric feature |
PetnowDetectionStatus.NoObject | No pet detected in the frame |
Distance Issues
| Status | Description |
|---|---|
PetnowDetectionStatus.TooClose | Camera is too close to the pet |
PetnowDetectionStatus.TooFarAway | Camera is too far from the pet |
Lighting Issues
| Status | Description |
|---|---|
PetnowDetectionStatus.TooBright | Too much light in the environment |
PetnowDetectionStatus.TooDark | Not enough light in the environment |
PetnowDetectionStatus.ShadowDetected | Shadows detected on the pet's nose/face |
PetnowDetectionStatus.GlareDetected | Glare detected on the pet's nose/face |
Clarity Issues
| Status | Description |
|---|---|
PetnowDetectionStatus.TooBlurred | Image is too blurry for detection |
PetnowDetectionStatus.MotionBlurDetected | Motion blur detected |
PetnowDetectionStatus.DefocusedBlurDetected | Camera is out of focus |
Angle Issues
| Status | Description |
|---|---|
PetnowDetectionStatus.NotFrontFace | Pet's face is not directly facing the camera |
PetnowDetectionStatus.NotFrontNose | Pet's nose is not directly facing the camera |
Obstruction Issues
| Status | Description |
|---|---|
PetnowDetectionStatus.NoseNotFound | Dog's nose not detected in the frame |
PetnowDetectionStatus.FurDetected | Fur is obstructing the detection |
Authenticity Issues
| Status | Description |
|---|---|
PetnowDetectionStatus.FakeDetected | Spoof 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 imagesfaceImageFiles: 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>Navigation Implementation
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
- Configure Before Navigation: Always call
configureDetectionMode()before creating camera fragment - Handle Permissions: Implement proper camera permission handling
- Provide User Feedback: Show clear status messages and progress indicators
- Error Recovery: Implement retry mechanisms for failed detections
- Memory Management: Clean up fragment resources properly
- Testing: Test with various lighting conditions and pet positions
- 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.