# Face Detection TADI SDK - Flutter Plugin

A Flutter plugin that integrates TADI Face Detection SDK for both iOS and Android platforms, providing face detection, liveness verification, and biometric identity verification capabilities.

## Features

- **Real-time Face Detection**: Using Google ML Kit (Android) and TrueDepth camera (iOS)
- **Liveness Verification**: Prevents photo/video spoofing
- **Biometric Identity Verification**: Integration with government identity services
- **Multi-language Support**: English, Russian, and Uzbek
- **Flexible Result Formats**: Flat (simple) or Nested (structured)
- **Security Features**:
  - JWT signature validation
  - Nonce-based challenge-response
  - Root/Jailbreak detection
  - Mutual SSL/TLS support
- **UI Customization**: Full control over colors, fonts, and layout

## Platform Support

| Platform | Minimum Version |
|----------|----------------|
| Android  | API 21 (Android 7.0) |
| iOS      | iOS 13.0+ |

## Installation

Add this to your package's `pubspec.yaml` file:

```yaml
dependencies:
  facedetect_tadi_sdk: ^0.0.10
```

Then run:

```bash
flutter pub get
```

## Platform-Specific Setup

### Android Setup

1. **Native SDK**: The native Android SDK is bundled with this plugin via a local Maven repository. No external repository configuration or credentials are required.

   > **Note**: The plugin includes the TADI Face Detection SDK (v1.0.8) in the `android/maven-repo` directory, so you don't need to configure any external Maven repositories.

2. **Update Minimum SDK**: Ensure your `android/app/build.gradle` has:

```gradle
android {
    defaultConfig {
        minSdkVersion 24
    }
}
```

3. **Add Permissions**: Add these permissions to your `AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
```

Optional hardware features:
```xml
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
```


### iOS Setup

1. **Add TGFISBIN Framework**: You need to add the TGFISBIN.xcframework to your iOS project.

   **Option 1: Using Swift Package Manager** (if available)

   Add to your iOS project in Xcode:
   ```
   https://gitlab.tadi.uz/tadi_public/TGFISBIN.git
   ```

   **Option 2: Manual Framework Integration**

   - Place `TGFISBIN.xcframework` in your `ios/` folder or `ios/Frameworks/`
   - Update the podspec to include vendored frameworks (see [ios/facedetect_tadi_sdk.podspec](ios/facedetect_tadi_sdk.podspec))

2. **Add Permissions**: Add these entries to your `Info.plist`:

```xml
<key>NSCameraUsageDescription</key>
<string>We need camera access to verify your identity and scan your documents</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access to select identity documents</string>
```

3. **Update Deployment Target**: Ensure your iOS deployment target is at least 13.0 in Xcode.

## Quick Start

### Basic Usage

```dart
import 'package:facedetect_tadi_sdk/facedetect_tadi_sdk.dart';

// Create SDK instance
final sdk = FacedetectTadiSdk();

// Configure SDK
final config = SdkConfig(
  accessToken: 'YOUR_ACCESS_TOKEN',
  language: 'en',  // 'en', 'ru', or 'uz'
  useNative: true,
);

// Start face detection
final result = await sdk.startFaceDetection(config);

if (result != null) {
  print('Name: ${result.firstName} ${result.lastName}');
  print('Document: ${result.docSeria} ${result.docNumber}');
  print('PINFL: ${result.pinfl}');
  print('Birth Date: ${result.birthDate}');
  print('Score: ${result.score}');
} else {
  print('Verification cancelled or failed');
}
```

### Advanced Configuration

```dart
final config = SdkConfig(
  accessToken: 'YOUR_ACCESS_TOKEN',
  baseUrl: 'https://your-api-url.com/',
  language: 'en',
  useNative: true,
  resultFormat: ResultFormat.flat,
  privacyPolicyUrl: 'https://yourcompany.com/privacy',
  bankId: 'YOUR_BANK_ID',  // Optional: Bank identifier

  // UI Customization
  uiConfig: UiConfig(
    logoUrl: 'https://yourcdn.com/logo.png',
    fontFamily: 'YourFont-Regular',
    inputShape: 'rounded',
    inputBorderStyle: 'solid',
    inputSize: 'medium',
    inputColor: '#FFFFFF',
    buttonColor: '#007AFF',
    linkColor: '#007AFF',
    elementColor: '#1C1C1E',
    layoutScale: 'medium',
    hideBackButton: false,
  ),
);
```

### With Mutual SSL/TLS

For enhanced security with client certificates:

```dart
final config = SdkConfig(
  accessToken: 'YOUR_ACCESS_TOKEN',
  enableMutualSSL: true,
  clientCertificateBase64: 'BASE64_ENCODED_P12_CERTIFICATE',
  clientCertificatePassword: 'certificate_password',
);
```

## API Reference

### SdkConfig

Configuration object for the SDK:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `accessToken` | String | ✅ | API authentication token |
| `baseUrl` | String | ❌ | Backend service URL (default: https://face.mbabm.uz/) |
| `language` | String | ❌ | UI language: 'en', 'ru', or 'uz' (default: 'uz') |
| `useNative` | bool | ❌ | Use native face detection (default: true) |
| `resultFormat` | ResultFormat | ❌ | Result format: flat or nested (default: flat) |
| `privacyPolicyUrl` | String | ❌ | Custom privacy policy URL |
| `jwtSecretKey` | String | ❌ | HMAC secret key for JWT validation |
| `jwtPublicKey` | String | ❌ | RSA public key for JWT validation |
| `clientCertificateBase64` | String | ❌ | P12 certificate (Base64) for mTLS |
| `clientCertificatePassword` | String | ❌ | Password for P12 certificate |
| `enableMutualSSL` | bool | ❌ | Enable mutual SSL authentication |
| `bankId` | String | ❌ | Bank identifier for tracking purposes |
| `uiConfig` | UiConfig | ❌ | UI customization options |

### FaceDetectionResult

Result object containing verification data:

```dart
class FaceDetectionResult {
  // Identity
  String? id;
  String? pin;
  String? pinfl;
  String? transactionId;

  // Names
  String? firstName;
  String? lastName;
  String? patronym;
  String? name;

  // Document Information
  String? docSeria;
  String? docNumber;
  String? dateIssue;
  String? dateExpiry;

  // Demographics
  String? gender;
  String? genderName;
  String? birthDate;
  String? birthPlace;
  String? citizenship;
  String? citizenshipName;

  // Verification
  double? score;  // Confidence score (0.0 - 1.0)

  // Media (Base64 encoded)
  String? photo;
  String? signature;
  String? capture;

  // Additional Documents
  List<DocumentInfo>? documents;
  List<ForeignDocument>? foreignDocuments;
}
```

### UiConfig

UI customization options:

```dart
UiConfig(
  logoUrl: 'https://example.com/logo.png',
  fontFamily: 'CustomFont-Regular',
  inputShape: 'rounded',  // 'rounded' or 'standard'
  inputBorderStyle: 'solid',  // 'solid', 'dashed', 'dotted'
  inputSize: 'medium',  // 'small', 'medium', 'large'
  inputColor: '#FFFFFF',
  buttonColor: '#007AFF',
  linkColor: '#007AFF',
  elementColor: '#1C1C1E',
  layoutScale: 'medium',  // 'small', 'medium', 'large'
  hideBackButton: false,
  hideDateOfBirthClear: false,
)
```

## Result Formats

### Flat Format (Default)

Returns a simple, flat structure with all fields at the top level:

```dart
final config = SdkConfig(
  accessToken: 'TOKEN',
  resultFormat: ResultFormat.flat,
);

final result = await sdk.startFaceDetection(config);
print(result?.firstName);  // Direct access
print(result?.docNumber);
```

### Nested Format

Returns a hierarchical, structured format (useful for complex integrations):

```dart
final config = SdkConfig(
  accessToken: 'TOKEN',
  resultFormat: ResultFormat.nested,
);

// Note: Nested format support is available on the native side
// but requires additional mapping in Flutter layer
```

## Handling Results

### Display Photo

```dart
import 'dart:convert';
import 'package:flutter/material.dart';

Widget buildPhoto(FaceDetectionResult result) {
  if (result.photo != null && result.photo!.isNotEmpty) {
    return Image.memory(
      base64Decode(result.photo!),
      height: 200,
      fit: BoxFit.contain,
    );
  }
  return Icon(Icons.person, size: 200);
}
```

### Validate Score

```dart
void handleResult(FaceDetectionResult result) {
  if (result.score != null) {
    if (result.score! >= 0.9) {
      print('✅ High confidence verification');
    } else if (result.score! >= 0.7) {
      print('⚠️ Medium confidence - manual review recommended');
    } else {
      print('❌ Low confidence - verification failed');
    }
  }
}
```

## Error Handling

```dart
try {
  final result = await sdk.startFaceDetection(config);

  if (result == null) {
    // User cancelled or verification failed
    print('Verification cancelled');
  } else {
    // Success
    print('Verification successful');
  }
} on PlatformException catch (e) {
  // Handle platform-specific errors
  print('Error: ${e.code} - ${e.message}');

  switch (e.code) {
    case 'NO_ACTIVITY':
      print('Activity not available');
      break;
    case 'ALREADY_RUNNING':
      print('Face detection already in progress');
      break;
    case 'SDK_ERROR':
      print('SDK initialization error');
      break;
    default:
      print('Unknown error');
  }
}
```

## Example App

See the [example](example/) directory for a complete demo app that shows:
- Configuration options
- Starting face detection
- Handling results
- Displaying photos
- Error handling

Run the example:

```bash
cd example
flutter run
```

## Troubleshooting

### Android Issues

**Build fails with "Could not find uz.tadi:facedetectsdk:1.0.4"**
- Ensure `gradle.properties` has the correct Nexus credentials
- Check your internet connection

**Face detection doesn't start**
- Verify camera permissions are granted
- Check that the device is not rooted
- Ensure access token is valid

### iOS Issues

**Build fails with "Module 'TGFISBIN' not found"**
- Ensure TGFISBIN.xcframework is properly added to your project
- Run `pod install` in the ios folder
- Clean build folder in Xcode (Cmd+Shift+K)

**Completion returns nil**
- Check if running on a real device (not simulator)
- Verify device is not jailbroken
- Ensure camera permissions are granted

## Platform-Native Documentation

For detailed information about the native SDKs:
- **Android**: See [README.android.md](README.android.md)
- **iOS**: See [README.ios.md](README.ios.md)

## Security Considerations

- Never hardcode access tokens in your app
- Store tokens securely using flutter_secure_storage or similar
- Use mutual SSL/TLS in production
- Validate JWT responses for additional security
- Never commit certificates or credentials to version control

## License

See [LICENSE](LICENSE) file for details.

## Support

For issues and questions:
- Create an issue in the repository
- Contact TADI support team
