Last active
December 2, 2021 13:21
-
-
Save surajsau/33a5730466874a7f4b6d1bb07b22e896 to your computer and use it in GitHub Desktop.
Google Translate Draw with Jetpack Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is a sample note since first file isn't being recognised in Medium. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sealed class DrawEvent { | |
// MotionEvent.ACTION_DOWN | |
data class Down(val x: Float, val y: Float): DrawEvent() | |
// MotionEvent.ACTION_MOVE | |
data class Move(val x: Float, val y: Float): DrawEvent() | |
// MotionEvent.ACTION_UP | |
object Up: DrawEvent() | |
} | |
@Composable | |
fun TranslateScreen() { | |
.. | |
DrawSpace( | |
onDrawEvent = { event -> .. } | |
) | |
} | |
@Composable | |
fun DrawSpace( | |
onDrawEvent: (DrawEvent) -> Unit, | |
.. | |
) { | |
.. | |
Canvas( | |
modifier = modifier | |
.pointerInteropFilter { event -> | |
val drawEvent = when (event.action) { | |
MotionEvent.ACTION_DOWN -> DrawEvent.Down(event.x, event.y) | |
MotionEvent.ACTION_MOVE -> DrawEvent.Move(event.x, event.y) | |
MotionEvent.ACTION_UP -> DrawEvent.Up | |
else -> null | |
} | |
drawEvent?.let { onDrawEvent.invoke(it) } | |
return true | |
} | |
) {..} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private val remoteModelManager = RemoteModelManager.getInstance() | |
fun checkModelAvailability() { | |
this.remoteModelManager | |
.isModelDownloaded(this.recognitionModel) | |
.addOnSuccessListener { isDownloaded -> | |
if (isDownloaded) | |
// model already available locally | |
else | |
// model needs to be downloaded | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private var recordStrokeJob: Job? = null | |
private const val DEBOUNCE_DELAY = 500L | |
when (event) { | |
/* | |
cancel previously running Job to prevent it | |
while in delay() | |
*/ | |
is DrawEvent.Down -> { | |
recordStrokeJob?.cancel() | |
.. | |
}, | |
/* | |
add a delay before DigitalInkRecognizer.recognizer() | |
*/ | |
is DrawEvent.Up -> { | |
.. | |
this.recordStrokeJob = /* CoroutineScope */.launch { | |
delay(DEBOUNCE_DELAY) | |
recognizer.recognize(..) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val stroke: Ink.Stroke = this.strokeBuilder.build() | |
val inkBuilder = Ink.builder() | |
inkBuilder.addStroke(stroke) | |
this.recognizer.recognize(inkBuilder.build()) | |
.addOnCompleteListener { .. } | |
.addOnSuccessListener { result -> .. } | |
.addOnFailureListener { .. } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun downloadModel() { | |
val downloadConditions = DownloadConditions.Builder() | |
/** | |
add download conditions if required | |
.requireWifi() | |
.requireCharging() | |
*/ | |
.build() | |
this.remoteModelManager | |
.downloadModel(this.recognitionModel, downloadConditions) | |
.addOnSuccessListener { | |
// model has been downloaded and is now available locally | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private sealed class DrawEvent { | |
data class MoveTo(val x: Float, val y: Float): DrawEvent() | |
data class CurveTo(val prevX: Float, val prevY: Float, val x: Float, val y: Float): DrawEvent() | |
} | |
@Composable | |
fun DrawSpace( | |
.. | |
) { | |
val path = remember { Path() } | |
var drawEvent by remember { mutableStateOf<DrawEvent?>(null) } | |
Canvas( | |
modifier = Modifier | |
.pointerInteropFilter { event -> | |
when (event.action) { | |
MotionEvent.ACTION_DOWN -> { | |
drawPath = DrawPath.MoveTo(event.x, event.y) | |
} | |
MotionEvent.ACTION_MOVE -> { | |
val prevX = when (drawEvent) { | |
is DrawPath.MoveTo -> (drawPath as DrawPath.MoveTo).x | |
is DrawPath.CurveTo -> (drawPath as DrawPath.CurveTo).x | |
} | |
val prevY = when (drawEvent) { .. } | |
drawPath = DrawPath.CurveTo(prevX, prevY, event.x, event.y) | |
} | |
else -> { /* do nothing */ } | |
} | |
return true | |
} | |
) { | |
if (drawEvent == null) | |
return | |
when (drawEvent) { | |
is DrawEvent.MoveTo -> { | |
// start drawing a new subpath | |
val (x, y) = drawPath as DrawPath.MoveTo | |
path.moveTo(x, y) | |
} | |
is DrawEvent.CurveTo -> { | |
// continue drawing the subpath as a smooth curve | |
val (prevX, prevY, x, y) = drawPath as DrawPath.CurveTo | |
path.quadraticBezierTo(prevX, prevY, (x + prevX)/2, (y + prevY)/2) | |
} | |
} | |
drawPath(path = path, ..) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private val recognitionModel: DigitalInkRecognitionModel | |
= DigitalInkRecognitionModel | |
.builder(DigitalInkRecognitionModelIdentifier.JA) // for Japanese language | |
.build() | |
private val recognizer: DigitalInkRecognizer | |
= DigitalInkRecognition.getClient( | |
DigitalInkRecognizerOptions | |
.builder(this.recognitionModel) | |
.build() | |
) | |
private var strokeBuilder: Ink.Stroke.Builder = Ink.Stroke.builder() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
when (event) { | |
/* | |
add coordinates obtained from MotionEvent.ACTION_MOVE, | |
MotionEvent.ACTION_DOWN to Ink.Stroke | |
*/ | |
is DrawEvent.Down, is DrawEvent.Move -> { | |
val point = Ink.Point.create(event.x, event.y) | |
strokeBuilder.addPoint(point) | |
} | |
is DrawEvent.Up -> { | |
// build stroke from the drawn points | |
val stroke = strokeBuilder.build() | |
val inkBuilder = Ink.builder() | |
inkBuilder.addStroke(stroke) | |
recognizer.recognize(inkBuilder.build()) | |
.addOnCompleteListener { | |
// reset strokeBuilder for further drawings | |
strokeBuilder = Ink.Stroke.builder() | |
} | |
.addOnSuccessListener { result -> | |
val predictions = result.candidates.map { it.text } | |
// choose first value the main prediction | |
// display other predictions as suggestions if required | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Composable | |
fun TranslateScreen() { | |
val lifecycleOwner = LocalLifecyleOwner.current | |
DisposableEffect(Unit) { | |
val lifecycleObserver = LifecycleEventObserver { _, event -> | |
if (event == Lifecycle.Event.ON_STOP) { | |
// close Translator & Recognizer here | |
} | |
} | |
lifecycleOwner.lifecycle.addObserver(lifecycleObserver) | |
onDispose { lifecycleOwner.lifecycle.removeObserver(lifecycleObserver) } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun record(event: MotionEvent) { | |
val point = Ink.Point.create(event.x, event.y) | |
this.strokeBuilder.addPoint(point) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private val translator: Translator | |
= Translation.getClient( | |
TranslatorOptions.Builder() | |
.setSourceLanguage(TranslateLanguage.JAPANESE) | |
.setTargetLanguage(TranslateLanguage.ENGLISH) | |
.build() | |
) | |
fun checkIfModelIsDownloaded() { | |
val downloadConditions = DownloadConditions.Builder() | |
.. | |
.build() | |
this.translator.downloadModelIfNeeded(downloadConditions) | |
.addOnSuccessListener { /* model is available locally now */ } | |
} | |
fun translate(text: String) { | |
this.translator.translate(text) | |
.addOnSuccessListener { result -> /* string result is returned */ } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment