Skip to content

Commit

Permalink
Merge pull request #69 from what3words/task/MT-7043-Update-README-int…
Browse files Browse the repository at this point in the history
…roducing-new-ways-to-scan-what3words-address-with-W3WImageDataSource

Update README
  • Loading branch information
phpduy authored Dec 30, 2024
2 parents 77f812a + e4b727c commit 56ba0bb
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 178 deletions.
302 changes: 184 additions & 118 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,164 +2,230 @@

[![Maven Central](https://img.shields.io/maven-central/v/com.what3words/w3w-android-ocr-components.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.what3words%22%20AND%20a:%22w3w-android-ocr-components%22)

### Android minumum SDK support
[![Generic badge](https://img.shields.io/badge/minSdk-24-green.svg)](https://developer.android.com/about/versions/marshmallow/android-7.0/)


An Android library to scan what3words address using [MLKit V2](https://developers.google.com/ml-kit/vision/text-recognition/v2/android).

<img src="https://github.com/what3words/w3w-android-ocr-components/blob/main/assets/ocr-component-demo.gif" width=40% height=40%>

To obtain an API key, please visit [https://what3words.com/select-plan](https://what3words.com/select-plan) and sign up for an account.
To obtain an API key, please [sign up here](https://what3words.com/select-plan).

### Gradle
### Android minimum SDK support
[![Generic badge](https://img.shields.io/badge/minSdk-24-green.svg)](https://developer.android.com/about/versions/marshmallow/android-7.0/)

### Gradle Dependency
```
implementation 'com.what3words:w3w-android-ocr-components:$latest'
```

## Sample using w3w-android-wrapper library
## Sample App

[ocr-sample](https://github.com/what3words/w3w-android-samples/tree/main/ocr-sample)
This repository includes a [sample app](https://github.com/what3words/w3w-android-samples/tree/main/ocr-sample) demonstrating the usage of the what3words OCR component.

## Usage

There are two ways to use our MLKit OCR Component:

1. As an Activity, **MLKitOcrScanActivity**, that should be used as an activity for result, which have minimum setup but doesn't allow style customisation. Our library handles all lifecycle and scan flow and will return the selected scanned three word address. Custom localisation and accessibility are available.

2. Using our Jetpack Compose Composable **W3WOcrScanner**, will allow all the above, but the results are returned as a callback (selection and errors) and will enable styling customisation, allowing to override all styles used on our composable with just a couple of extra steps to setup.

### Using MLKitOcrScanActivity (#1)

```Kotlin
class MainActivity : AppCompatActivity() {

private val resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
when {
//registerForActivityResult success with result
result.resultCode == Activity.RESULT_OK && result.data?.hasExtra(BaseOcrScanActivity.SUCCESS_RESULT_ID) == true -> {
val suggestion =
result.data!!.serializable<SuggestionWithCoordinates>(BaseOcrScanActivity.SUCCESS_RESULT_ID)
//TODO: Handle suggestion scanned
}
//registerForActivityResult canceled with error
result.resultCode == Activity.RESULT_CANCELED && result.data?.hasExtra(
BaseOcrScanActivity.ERROR_RESULT_ID
) == true -> {
val error =
result.data!!.getStringExtra(BaseOcrScanActivity.ERROR_RESULT_ID)
//TODO: Handle error
}
//registerForActivityResult canceled by user.
else -> {
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
...
//Options to filter the OCR scanning or like this example providing current location for more accurate results/distances to three word addresses.
val options = AutosuggestOptions().apply {
this.focus = Coordinates(51.23, 0.1)
}

//Per default the scanned three word address will not return coordinate information, if you set returnCoordinates to true when instanciating a new MLKitOcrScanActivity, it will return coordinates and this might results in charge against your API Key.
val returnCoordinates = true

//MLKitOcrScanActivity.newInstanceWithApi allows to provide all strings to be used internally for localisation and accessibility propuses. This should be used on a click actions, i.e: button click.
val intent = MLKitOcrScanActivity.newInstanceWithApi(
context = this,
mlKitLibrary = com.google.mlkit.vision.text.TextRecognizerOptionsInterface.LATIN,
apiKey = "YOUR_API_KEY_HERE",
options = options,
returnCoordinates = returnCoordinates,
scanStateFoundTitle = "Custom found title"
)
try {
resultLauncher.launch(intent)
} catch (e: ExceptionInInitializerError) {
//TODO: Handle Error
}
}
}
Adding a `W3WOcrScanner` to your app:

```kotlin
W3WOcrScanner(
ocrScanManager = ocrScanManager,
onDismiss = {
// Handle scanner dismissal
},
onSuggestionSelected = { suggestion ->
// Handle address selection
},
onError = { error ->
// Handle the error
},
)
```

### Using W3WOcrScanner Composable (#2)

```Kotlin
class MainActivity : ComponentActivity() {
private lateinit var ocrWrapper: W3WOcrWrapper
<details>
<summary>Creating an OcrScanManager</summary>

override fun onDestroy() {
super.onDestroy()
if (::ocrWrapper.isInitialized) ocrWrapper.stop()
}

override fun onCreate(savedInstanceState: Bundle?) {
//This example uses Latin MLKit library, check MLKit documentation of how to instanciate other libraries like Korean, Japanese, Devanagari or Chinese.
val textRecognizer = com.google.mlkit.vision.text.TextRecognizerOptionsInterface.LATIN
OcrScanManager encapsulates the scanner’s state and logic leveraging `W3WImageDataSource` and `W3WTextDataSource` for scanning and validating addresses.

//Options to filter the OCR scanning or like this example providing current location for more accurate results/distances to three word addresses.
val options = AutosuggestOptions().apply {
this.focus = Coordinates(51.23, 0.1)
}
```kotlin
val w3WImageDataSource = W3WMLKitImageDataSource.create(
context = context,
recognizerOptions = TextRecognizerOptionsInterface.LATIN
)

val w3WTextDataSource = W3WApiTextDataSource.create(context, W3W_API_KEY)

val ocrScanManager = rememberOcrScanManager(
w3wImageDataSource = w3WImageDataSource,
w3wTextDataSource = w3WTextDataSource,
)

```
**Setup Options**

One of the significant enhancements in v2.0 is the flexibility to choose and configure different data sources for the OCR component.

- **Online Setup**: Use MLKit for scanning and W3WApiTextDataSource for online validation.
- **Offline Setup**: Replace W3WMLKitImageDataSource with W3WTesseractImageDataSource for offline scanning and W3WSdkTextDataSource for offline validation.
> (Offline setup requires additional licensing; contact us for details.)
</details>
//Per default the scanned three word address will not return coordinate information, if you set returnCoordinates to true when instanciating a new MLKitOcrScanActivity, it will return coordinates and this might results in charge against your API Key.
val returnCoordinates = true

val dataProvider = What3WordsV3("YOUR_API_KEY_HERE", this)
ocrWrapper = W3WOcrMLKitWrapper(this, textRecognizer)

setContent {
YourTheme {
W3WOcrScanner(
ocrWrapper,
dataProvider = dataProvider,
modifier = Modifier.fillMaxSize(),
options = options,
returnCoordinates = returnCoordinates,
onError = { error ->
//TODO: Handle error
},
onDismiss = {
//TODO: Dismissed by user, hide W3WOcrScanner using AnimatedVisibility or finish activity.
},
onSuggestionSelected = { scannedSuggestion ->
//TODO: Use scanned three word address info, hide W3WOcrScanner using AnimatedVisibility or finish activity.
}
)
}
<details>
<summary>Using OcrScannerState</summary>

If you prefer more control over the scanner's state, you can use `OcrScannerState` directly, instead of using `OcrScanManager`. This allows you to manage the state externally and integrate the component into your existing architecture.

#### Example Usage
```kotlin
val ocrScannerState = remember { OcrScannerState() }

W3WOcrScanner(
ocrScannerState = ocrScannerState,
onError = { error ->
// Handle the error
},
onDismiss = {
// Handle scanner dismissal
},
onFrameCaptured = { image ->
// Scanner has captured a frame, you will need to implement your functions to detect the what3words addresses in the frame and then update the ocrScannerState

CompletableDeferred<Unit>().apply {
// Signal completion when processing is done
complete(Unit)
}
}
}
},
onSuggestionSelected = { suggestion ->
// Handle address selection
},
)
```

### Styling W3WOcrScanner (#2)
#### Feature Comparison: OcrScanManager vs. OcrScannerState
| Feature/Aspect | **OcrScanManager** | **OcrScannerState** |
|-------------------------------|-----------------------------------------------------------------|----------------------------------------------------------|
| **Ease of Use** | Encapsulates scanner state and logic easier to use out of the box. | Provides more granular control but requires extra setup. |
| **State Management** | Internal state management is ideal for simple integrations. | External state management is better for complex apps. |
| **Customizability** | Limited to predefined functionalities. | High, allows deep customization and integration. |
| **Use Case** | Quick implementation without managing internal details. | Advanced use cases needing dynamic or external control. |
| **Flexibility** | Limited flexibility.

```Kotlin
</details>

<details>
<summary>Styling W3WOcrScanner</summary>

You can customise the appearance of the `W3WOcrScanner` by providing parameters for strings, colours, and text styles.

```kotlin
W3WOcrScanner(
...
//optional if you want to override any string of the scanner composable, to allow localisation and accessibility.
scannerStrings = W3WOcrScannerDefaults.defaultStrings(
scanStateFoundTitle = stringResource(id = R.string.scan_state_found),
),
//optional if you want to override any colors of the scanner composable.
scannerColors = W3WOcrScannerDefaults.defaultColors(
bottomDrawerBackground = W3WTheme.colors.background
),
//optional if you want to override any text styles.
scannerTextStyles = W3WOcrScannerDefaults.defaultTextStyles(
stateTextStyle = W3WTheme.typography.headline
),
//optional if you want to override any colors of the scanned list item composable.
suggestionColors = SuggestionWhat3wordsDefaults.defaultColors(
background = W3WTheme.colors.background
),
//optional if you want to override any text styles of the scanned list item composable.
suggestionTextStyles = SuggestionWhat3wordsDefaults.defaultTextStyles(
wordsTextStyle = W3WTheme.typography.headline
)
)
```

</details>

<details>
<summary>Camera Permission</summary>

The OCR component requires camera permission to function. Add the following permission to your app’s `AndroidManifest.xml` file:

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

</details>

## Migrating to v2.0

For guidance on upgrading from v1.x to v2.0, see the migration steps below:

---

### Introduction to the Core Library

The [Core Library](https://github.com/what3words/w3w-core-library) introduces essential models and interfaces that ensure consistency across various what3words libraries. In v2.0 of the OCR component, many existing models have been replaced by those from the Core Library. For more details on the new models, please refer to this [README](https://github.com/what3words/w3w-android-wrapper?tab=readme-ov-file#introduce-of-the-core-library).

---

### Migration steps

<details>
<summary><b>Step 1: Update Dependencies</b></summary>

Update your app's dependencies to use the latest version of the what3words OCR components:

```gradle
implementation 'com.what3words:w3w-android-ocr-components:$latest'
```

Ensure your build file is synced and the dependencies are updated to avoid conflicts.

</details>

<details>
<summary><b>Step 2: Replace Deprecated Models</b></summary>

In v2.0, many models from v1.x have been deprecated in favour of the new models provided by the Core Library. Unfortunately, there is no automated process for this migration. You'll need to manually replace the old models with their counterparts from the Core Library.

</details>

<details>
<summary><b>Step 3: Replace <code>W3WOcrMLKitWrapper</code> with <code>OcrScanManager</code></b></summary>

In v1.x, you may have used the following setup:

```kotlin
val dataProvider = What3WordsV3(W3W_API_KEY, context)
ocrWrapper = W3WOcrMLKitWrapper(context, textRecognizer)

W3WOcrScanner(
ocrWrapper,
dataProvider = dataProvider,
....
)
```

In v2.0, this has been simplified with the introduction of OcrScanManager. Here’s the updated implementation:

```kotlin
val w3WImageDataSource = W3WMLKitImageDataSource.create(
context = context,
recognizerOptions = TextRecognizerOptionsInterface.LATIN
)

val w3WTextDataSource = W3WApiTextDataSource.create(context, W3W_API_KEY)

val ocrScanManager = rememberOcrScanManager(
w3wImageDataSource = w3WImageDataSource,
w3wTextDataSource = w3WTextDataSource,
)

W3WOcrScanner(
ocrScanManager = ocrScanManager,
...
)
```

The OcrScanManager encapsulates scanning logic and state management, providing a more streamlined API.

</details>

<details>
<summary><b>Step 4: Removal of <code>MLKitOcrScanActivity</code></b></summary>

MLKitOcrScanActivity has been deprecated. For standalone activity support, leverage Jetpack Compose interoperability. Learn more in [this tutorial](https://developer.android.com/develop/ui/compose/migrate/interoperability-apis/compose-in-views).
</details>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.google.android.gms.common.moduleinstall.ModuleInstall
import com.google.android.gms.common.moduleinstall.ModuleInstallClient
import com.google.mlkit.vision.text.TextRecognition
import com.google.mlkit.vision.text.TextRecognizer
import com.google.mlkit.vision.text.TextRecognizerOptionsInterface
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
Expand Down Expand Up @@ -122,6 +123,14 @@ class W3WMLKitImageDataSource internal constructor(
}

companion object {
/**
* Creates a new instance of [W3WMLKitImageDataSource].
*
* @param context The context of the application.
* @param recognizerOptions The options [TextRecognizerOptionsInterface] for configuring the text recognizer.
*
* @return A new instance of W3WMLKitImageDataSource.
*/
@JvmStatic
fun create(
context: Context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

/**
* Scan [image] [Bitmap] for one or more what3words addresses using MLKit.
* Scan [image] [Bitmap] for one or more possible what3words addresses using MLKit.
*
* @param image the [Bitmap] that the scanner should use to find possible what3words addresses.
* @param onScanning the callback when it starts to scan image for text.
Expand Down
Loading

0 comments on commit 56ba0bb

Please sign in to comment.