Skip to content

Commit 97cdea7

Browse files
committed
Add Ink Reco SDK sample
1 parent 79ba4af commit 97cdea7

36 files changed

+910
-0
lines changed

InkRecognition/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.iml
2+
.gradle
3+
local.properties
4+
.idea
5+
.DS_Store
6+
build
7+
captures
8+
.externalNativeBuild

InkRecognition/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
services: cognitive-services, inkrecognition
3+
platforms: java
4+
---
5+
6+
# Ink Recognition SDK Sample ##
7+
8+
Sample code for the Ink Recognizer Cognitive Service SDK which provides easy access to a cloud-based REST API to analyze and recognize digital ink content.
9+
- Recognizes text drawn
10+
- Shows how to set language on the request
11+
- Shows how to set the ink content type on request
12+
13+
## Features
14+
15+
This project framework provides examples for using the Ink Recognition SDK.
16+
17+
## Getting Started
18+
19+
### Prerequisites
20+
21+
- Android 8.0 or later
22+
- You must have a Cognitive Services API account. Please refer the [Ink Recognizer API](https://docs.microsoft.com/en-us/azure/cognitive-services/ink-recognizer/overview) if you don't have one already.
23+
24+
### Quickstart
25+
26+
To get these samples running locally, simply get the pre-requisites above, then:
27+
28+
1. git clone https://github.com/Azure-Samples/cognitive-services-java-sdk-samples.git
29+
2. cd cognitive-services-java-sdk-samples/InkRecognition
30+
3. Set the key in the app/src/main/java/CognitiveServices/Ink/Recognition/NoteTaker.java
31+
4. Run 'gradlew build' in the terminal at the root of InkRecognition to build the project
32+
5. Run 'gradlew assemble' in the terminal at the root of InkRecognition for the release apk under InkRecognition/app/build/outputs/apk/release
33+
6. Deploy the APK to the phone or emulator using Android Developer Tools
34+
35+
---
36+
37+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.

InkRecognition/app/build.gradle

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
apply plugin: 'com.android.application'
2+
3+
android {
4+
compileSdkVersion 28
5+
defaultConfig {
6+
applicationId "CognitiveServices.Ink.Recognition"
7+
minSdkVersion 26
8+
targetSdkVersion 28
9+
versionCode 1
10+
versionName "1.0"
11+
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12+
}
13+
compileOptions {
14+
sourceCompatibility 1.8
15+
targetCompatibility 1.8
16+
}
17+
buildTypes {
18+
release {
19+
minifyEnabled false
20+
}
21+
}
22+
}
23+
24+
dependencies {
25+
implementation fileTree(dir: 'libs', include: ['*.jar'])
26+
implementation 'com.android.support:appcompat-v7:28.0.0'
27+
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
28+
implementation 'com.android.support:design:28.0.0'
29+
implementation 'com.squareup.okhttp3:okhttp:4.2.0'
30+
implementation 'io.projectreactor:reactor-core:3.2.3.RELEASE'
31+
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.9'
32+
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.9'
33+
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.9'
34+
implementation project(':inkrecognizer-sdk')
35+
testImplementation 'junit:junit:4.12'
36+
androidTestImplementation 'com.android.support.test:runner:1.0.2'
37+
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
38+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="CognitiveServices.Ink.Recognition">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
7+
<application
8+
android:allowBackup="true"
9+
android:icon="@mipmap/ic_launcher"
10+
android:label="@string/app_name"
11+
android:roundIcon="@mipmap/ic_launcher_round"
12+
android:supportsRtl="true"
13+
android:theme="@style/AppTheme"
14+
android:fullBackupContent="@xml/backup_descriptor">
15+
<activity
16+
android:name=".MainActivity"
17+
android:label="@string/app_name"
18+
android:theme="@style/AppTheme.NoActionBar">
19+
<intent-filter>
20+
<action android:name="android.intent.action.MAIN" />
21+
22+
<category android:name="android.intent.category.LAUNCHER" />
23+
</intent-filter>
24+
</activity>
25+
</application>
26+
27+
</manifest>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package CognitiveServices.Ink.Recognition;
2+
3+
import com.azure.ai.inkrecognizer.InkPoint;
4+
5+
class InkPointImplementor implements InkPoint {
6+
7+
final private float x;
8+
final private float y;
9+
10+
InkPointImplementor(float x, float y) {
11+
this.y = y;
12+
this.x = x;
13+
}
14+
15+
public float getX() {
16+
return x;
17+
}
18+
19+
public float getY() {
20+
return y;
21+
}
22+
23+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package CognitiveServices.Ink.Recognition;
2+
3+
import com.azure.ai.inkrecognizer.*;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
class InkStrokeImplementor implements InkStroke {
9+
10+
final private long strokeId;
11+
final private List<InkPoint> inkPoints = new ArrayList<>();
12+
final private String language;
13+
final private InkStrokeKind kind;
14+
private static int num = 0;
15+
16+
InkStrokeImplementor() {
17+
this.strokeId = getNextNum();
18+
this.language = "en-US";
19+
this.kind = InkStrokeKind.UNKNOWN;
20+
}
21+
22+
public void addPoint(float x, float y) {
23+
InkPointImplementor point = new InkPointImplementor(x, y);
24+
inkPoints.add(point);
25+
}
26+
27+
private int getNextNum() {
28+
return ++num;
29+
}
30+
31+
public Iterable<InkPoint> getInkPoints() {
32+
return inkPoints;
33+
}
34+
35+
public InkStrokeKind getKind() {
36+
return kind;
37+
}
38+
39+
public long getId() {
40+
return strokeId;
41+
}
42+
43+
public String getLanguage() {
44+
return language;
45+
}
46+
47+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package CognitiveServices.Ink.Recognition;
2+
3+
import android.os.Bundle;
4+
import android.support.v7.app.AppCompatActivity;
5+
6+
public class MainActivity extends AppCompatActivity {
7+
8+
@Override
9+
protected void onCreate(Bundle savedInstanceState) {
10+
super.onCreate(savedInstanceState);
11+
try {
12+
NoteTaker noteTaker = new NoteTaker(this);
13+
setContentView(noteTaker);
14+
} catch (Exception e) {
15+
System.out.println("Error in ink analysis");
16+
}
17+
}
18+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package CognitiveServices.Ink.Recognition;
2+
3+
import android.content.Context;
4+
import android.graphics.Canvas;
5+
import android.graphics.Color;
6+
import android.graphics.Paint;
7+
import android.graphics.Path;
8+
import android.os.AsyncTask;
9+
import android.os.CountDownTimer;
10+
import android.util.DisplayMetrics;
11+
import android.view.MotionEvent;
12+
import android.view.View;
13+
import android.widget.Toast;
14+
import com.azure.ai.inkrecognizer.*;
15+
import com.azure.ai.inkrecognizer.model.InkDrawing;
16+
import com.azure.ai.inkrecognizer.model.InkRecognitionRoot;
17+
import com.azure.ai.inkrecognizer.model.InkWord;
18+
import reactor.core.publisher.Mono;
19+
20+
import java.util.ArrayList;
21+
22+
public class NoteTaker extends View {
23+
24+
private final Path path = new Path();
25+
private final Paint brush = new Paint();
26+
private InkStrokeImplementor stroke;
27+
private final InkRecognizerAsyncClient inkRecognizerAsyncClient;
28+
private CountDownTimer analysisTimer = null;
29+
private final ArrayList<InkStroke> strokes = new ArrayList<>();
30+
31+
public NoteTaker(Context context) throws Exception {
32+
super(context);
33+
String appKey = "<SUBSCRIPTION_KEY>";
34+
String destinationUrl = "https://api.cognitive.microsoft.com/inkrecognizer";
35+
inkRecognizerAsyncClient = new InkRecognizerClientBuilder()
36+
.credentials(new InkRecognizerCredentials(appKey))
37+
.endpoint(destinationUrl)
38+
.retryCount(3)
39+
.retryTimeout(300)
40+
.applicationKind(ApplicationKind.WRITING)
41+
.language("en-US")
42+
.unit(InkPointUnit.PIXEL)
43+
.unitMultiple(1)
44+
.serviceVersion(ServiceVersion.PREVIEW_1_0_0)
45+
.displayMetrics(getResources().getDisplayMetrics())
46+
.buildAsyncClient();
47+
DisplayMetrics metrics = getResources().getDisplayMetrics();
48+
System.out.println("DisplayMetrics: " + metrics.xdpi + ", " + metrics.ydpi);
49+
brush.setAntiAlias(true);
50+
brush.setColor(Color.BLACK);
51+
brush.setStyle(Paint.Style.STROKE);
52+
brush.setStrokeJoin(Paint.Join.ROUND);
53+
brush.setStrokeWidth(3.0f);
54+
}
55+
56+
private void startTimer() {
57+
//The next 2 variables are used for the inactivity timer which triggers recognition
58+
//after a certain period of inactivity.
59+
int milliSeconds = 2000;//Time to wait
60+
int countDownInterval = 1000;//interval
61+
NoteTaker noteTaker = this;
62+
analysisTimer = new CountDownTimer(milliSeconds, countDownInterval) {
63+
public void onTick(long millFinish) {
64+
}
65+
66+
public void onFinish() {
67+
if (strokes.size() != 0) {
68+
new recognizeInkTask().execute(noteTaker);
69+
}
70+
71+
}
72+
}.start();
73+
}
74+
75+
private static class recognizeInkTask extends AsyncTask<NoteTaker, Integer, NoteTaker> {
76+
77+
StringBuilder recognizedWords = new StringBuilder();
78+
79+
@Override
80+
protected NoteTaker doInBackground(NoteTaker... params) {
81+
NoteTaker noteTaker = params[0];
82+
try {
83+
if (noteTaker.strokes.size() != 0) {
84+
System.out.println();
85+
Mono<Response<InkRecognitionRoot>> response = noteTaker.inkRecognizerAsyncClient.recognizeInk(noteTaker.strokes);
86+
response.subscribe(r -> showResults(r), e -> e.printStackTrace());
87+
}
88+
} catch (Exception e) {
89+
e.printStackTrace();
90+
System.out.println("Error parsing response");
91+
}
92+
return noteTaker;
93+
}
94+
95+
@Override
96+
protected void onPostExecute(NoteTaker noteTaker) {
97+
Toast toast = Toast.makeText(noteTaker.getContext(), recognizedWords.toString(), Toast.LENGTH_LONG);
98+
toast.show();
99+
}
100+
101+
private void showResults(Response<InkRecognitionRoot> response) {
102+
if (response.status == 200) {
103+
104+
InkRecognitionRoot root = response.root;
105+
106+
recognizedWords.append("\r\nRecognized Text:\r\n");
107+
Iterable<InkWord> words = root.inkWords();
108+
for (InkWord word : words) {
109+
recognizedWords.append(word.recognizedText());
110+
recognizedWords.append(" ");
111+
}
112+
113+
Iterable<InkDrawing> drawings = root.inkDrawings();
114+
recognizedWords.append("\r\nRecognized Shapes:\r\n");
115+
for (InkDrawing drawing : drawings) {
116+
recognizedWords.append(drawing.recognizedShape().toString()).append("\r\n");
117+
}
118+
119+
}
120+
}
121+
122+
}
123+
124+
private void cancelTimer() {
125+
if (analysisTimer != null) {
126+
analysisTimer.cancel();
127+
}
128+
}
129+
130+
@Override
131+
public boolean onTouchEvent(MotionEvent event) {
132+
float x = event.getX();
133+
float y = event.getY();
134+
135+
switch (event.getAction()) {
136+
case MotionEvent.ACTION_DOWN:
137+
path.moveTo(x, y);
138+
stroke = new InkStrokeImplementor();
139+
stroke.addPoint(x, y);
140+
cancelTimer();
141+
return true;
142+
case MotionEvent.ACTION_MOVE:
143+
path.lineTo(x, y);
144+
stroke.addPoint(x, y);
145+
break;
146+
case MotionEvent.ACTION_UP:
147+
strokes.add(stroke);
148+
startTimer();
149+
break;
150+
default:
151+
return false;
152+
}
153+
postInvalidate();
154+
return false;
155+
}
156+
157+
@Override
158+
protected void onDraw(Canvas canvas) {
159+
canvas.drawPath(path, brush);
160+
}
161+
}

0 commit comments

Comments
 (0)