Skip to content

Commit 4b2667d

Browse files
committedMay 6, 2016
[src] Initial commit.
0 parents  commit 4b2667d

File tree

10 files changed

+631
-0
lines changed

10 files changed

+631
-0
lines changed
 

‎.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# EditorConfig helps developers define and maintain consistent
2+
# coding styles between different editors and IDEs
3+
# editorconfig.org
4+
5+
root = true
6+
7+
[*.{js,less}]
8+
end_of_line = lf
9+
charset = utf-8
10+
trim_trailing_whitespace = true
11+
insert_final_newline = true
12+
indent_style = space
13+
indent_size = 2

‎.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
node_modules
2+
*.class
3+
4+
# Mobile Tools for Java (J2ME)
5+
.mtj.tmp/
6+
7+
# Package Files #
8+
*.jar
9+
10+
build/
11+
VoiceModule.iml

‎.jshintrc

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"undef": "true",
3+
"browser": false,
4+
"esnext": true,
5+
"globals": {
6+
"console": true,
7+
"__filename": true,
8+
"__dirname": true,
9+
"require": true,
10+
"module": true,
11+
"exports": true,
12+
"setInterval": true,
13+
"clearInterval": true,
14+
"fetch": true,
15+
"await": true
16+
},
17+
"strict": false,
18+
"expr": true,
19+
"globalstrict": true,
20+
"quotmark": "single",
21+
"smarttabs": true,
22+
"trailing": true,
23+
"curly": true,
24+
"debug": false,
25+
"devel": false,
26+
"eqeqeq": true,
27+
"eqnull": true,
28+
"evil": true,
29+
"forin": false,
30+
"immed": true,
31+
"laxbreak": false,
32+
"newcap": true,
33+
"noarg": true,
34+
"noempty": false,
35+
"nonew": true,
36+
"nomen": false,
37+
"onevar": false,
38+
"plusplus": false,
39+
"undef": true,
40+
"sub": false,
41+
"white": false,
42+
"eqeqeq": false,
43+
"latedef": "nofunc",
44+
"sub": true
45+
}

‎README.md

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# React Native Voice
2+
A speech-to-text library for [React Native](https://facebook.github.io/react-native/).
3+
4+
**NOTE**, currently only supports Android. Contribute to make this a universal module!
5+
6+
# Install
7+
8+
```sh
9+
npm i react-native-voice --save
10+
```
11+
12+
## Android
13+
- In `android/setting.gradle`
14+
15+
```gradle
16+
...
17+
include ':VoiceModule', ':app'
18+
project(':VoiceModule').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-voice/android')
19+
```
20+
21+
- In `android/app/build.gradle`
22+
23+
```gradle
24+
...
25+
dependencies {
26+
...
27+
compile project(':VoiceModule')
28+
}
29+
```
30+
31+
- In `MainActivity.java`
32+
33+
```java
34+
35+
import com.facebook.react.ReactPackage;
36+
...
37+
import com.wenkesj.voice.VoicePackage; // <------ Add this!
38+
...
39+
40+
public class MainActivity extends ReactActivity {
41+
...
42+
@Override
43+
protected List<ReactPackage> getPackages() {
44+
return Arrays.<ReactPackage>asList(
45+
new MainReactPackage(),
46+
new VoicePackage() // <------ Add this!
47+
);
48+
}
49+
}
50+
```
51+
52+
# Usage
53+
54+
```javascript
55+
import Voice from 'react-native-voice';
56+
```
57+
58+
## Methods
59+
Accessible methods to perform actions.
60+
61+
Method Name | Description | Platform
62+
--------------------- | -------------------------------------------------------------------------------- | --------
63+
isAvailable(callback) | Checks whether a speech recognition service is available on the system. | Android
64+
start() | Starts listening for speech. Returns null if no error occurs. | Android
65+
stop() | Stops listening for speech. Returns null if no error occurs. | Android
66+
cancel() | Cancels the speech recognition. Returns null if no error occurs. | Android
67+
destroy() | Destroys the current SpeechRecognizer instance. Returns null if no error occurs. | Android
68+
isRecognizing() | Return if the SpeechRecognizer is recognizing. | Android
69+
70+
## Events
71+
Methods that are invoked when a native event emitted.
72+
73+
Event Name | Description | Event | Platform
74+
----------------------------- | ------------------------------------------------------ | ----------------------------------------------- | --------
75+
onSpeechStart(event) | Invoked when `.start()` is called without error. | `{ error: false }` | Android
76+
onSpeechRecognized(event) | Invoked when speech is recognized. | `{ value: [..., 'Speech recognized'] }` | Android
77+
onSpeechEnd(event) | Invoked when SpeechRecognizer stops recognition. | `{ error: false }` | Android
78+
onSpeechError(event) | Invoked when an error occurs. | `{ error: Description of error as string }` | Android
79+
onSpeechResults(event) | Invoked when SpeechRecognizer is finished recognizing. | `{ value: [..., 'Speech recognized'] }` | Android
80+
onSpeechPartialResults(event) | Invoked when any results are computed. | `{ value: [..., 'Partial speech recognized'] }` | Android
81+
onSpeechVolumeChanged(event) | Invoked when pitch that is recognized changed. | `{ value: pitch in dB }` | Android

‎android/build.gradle

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
apply plugin: 'com.android.library'
2+
3+
android {
4+
compileSdkVersion 23
5+
buildToolsVersion "23.0.2"
6+
7+
defaultConfig {
8+
minSdkVersion 15
9+
targetSdkVersion 23
10+
versionCode 1
11+
versionName "1.0"
12+
}
13+
buildTypes {
14+
release {
15+
minifyEnabled false
16+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17+
}
18+
}
19+
}
20+
21+
buildscript {
22+
repositories {
23+
jcenter()
24+
}
25+
dependencies {
26+
classpath 'com.android.tools.build:gradle:1.5.0'
27+
28+
// NOTE: Do not place your application dependencies here; they belong
29+
// in the individual module build.gradle files
30+
}
31+
}
32+
33+
allprojects {
34+
repositories {
35+
jcenter()
36+
}
37+
}
38+
39+
task clean(type: Delete) {
40+
delete rootProject.buildDir
41+
}
42+
43+
dependencies {
44+
compile fileTree(dir: 'libs', include: ['*.jar'])
45+
testCompile 'junit:junit:4.12'
46+
compile 'com.android.support:appcompat-v7:23.1.1'
47+
compile 'com.facebook.react:react-native:0.17.+'
48+
}

‎android/src/main/AndroidManifest.xml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2+
package="com.wenkesj.voice"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
<uses-sdk tools:overrideLibrary="com.facebook.react" />
6+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
7+
<uses-permission android:name="android.permission.INTERNET" />
8+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
package com.wenkesj.voice;
2+
3+
import android.content.Intent;
4+
import android.speech.RecognitionListener;
5+
import android.speech.RecognizerIntent;
6+
import android.speech.SpeechRecognizer;
7+
import android.os.Bundle;
8+
import android.os.Handler;
9+
10+
import com.facebook.react.modules.core.DeviceEventManagerModule;
11+
import com.facebook.react.bridge.WritableMap;
12+
import com.facebook.react.bridge.WritableArray;
13+
import com.facebook.react.bridge.Arguments;
14+
import com.facebook.react.bridge.ReactApplicationContext;
15+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
16+
import com.facebook.react.bridge.ReactMethod;
17+
import com.facebook.react.bridge.Callback;
18+
19+
import java.util.ArrayList;
20+
import java.util.HashMap;
21+
import java.util.Locale;
22+
import java.util.Map;
23+
import java.lang.Exception;
24+
import java.lang.Runnable;
25+
26+
import javax.annotation.Nullable;
27+
28+
public class VoiceModule extends ReactContextBaseJavaModule implements RecognitionListener {
29+
30+
final ReactApplicationContext reactContext;
31+
private SpeechRecognizer speech = null;
32+
private boolean isRecognizing = false;
33+
34+
public VoiceModule(ReactApplicationContext reactContext) {
35+
super(reactContext);
36+
this.reactContext = reactContext;
37+
}
38+
39+
@Override
40+
public String getName() {
41+
return "RCTVoice";
42+
}
43+
44+
@ReactMethod
45+
public void startSpeech(final String locale, final Callback callback) {
46+
final VoiceModule self = this;
47+
Handler mainHandler = new Handler(this.reactContext.getMainLooper());
48+
mainHandler.post(new Runnable() {
49+
50+
private String getLocale(String locale){
51+
if(locale != null && !locale.equals("")){
52+
return locale;
53+
}
54+
55+
return Locale.getDefault().toString();
56+
}
57+
58+
@Override
59+
public void run() {
60+
try {
61+
speech = SpeechRecognizer.createSpeechRecognizer(self.reactContext);
62+
} catch(Exception e) {
63+
callback.invoke(e);
64+
}
65+
66+
speech.setRecognitionListener(self);
67+
68+
final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
69+
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
70+
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, getLocale(locale));
71+
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 5);
72+
73+
speech.startListening(intent);
74+
isRecognizing = true;
75+
callback.invoke(false);
76+
}
77+
});
78+
}
79+
80+
@ReactMethod
81+
public void stopSpeech(final Callback callback) {
82+
Handler mainHandler = new Handler(this.reactContext.getMainLooper());
83+
mainHandler.post(new Runnable() {
84+
@Override
85+
public void run() {
86+
try {
87+
speech.stopListening();
88+
callback.invoke(false);
89+
} catch(Exception e) {
90+
callback.invoke(e);
91+
}
92+
}
93+
});
94+
}
95+
96+
@ReactMethod
97+
public void cancelSpeech(final Callback callback) {
98+
Handler mainHandler = new Handler(this.reactContext.getMainLooper());
99+
mainHandler.post(new Runnable() {
100+
@Override
101+
public void run() {
102+
try {
103+
speech.cancel();
104+
isRecognizing = false;
105+
callback.invoke(false);
106+
} catch(Exception e) {
107+
callback.invoke(e);
108+
}
109+
}
110+
});
111+
}
112+
113+
@ReactMethod
114+
public void destroySpeech(final Callback callback) {
115+
Handler mainHandler = new Handler(this.reactContext.getMainLooper());
116+
mainHandler.post(new Runnable() {
117+
@Override
118+
public void run() {
119+
try {
120+
speech.destroy();
121+
isRecognizing = false;
122+
callback.invoke(false);
123+
} catch(Exception e) {
124+
callback.invoke(e);
125+
}
126+
}
127+
});
128+
}
129+
130+
@ReactMethod
131+
public void isSpeechAvailable(final Callback callback) {
132+
final VoiceModule self = this;
133+
Handler mainHandler = new Handler(this.reactContext.getMainLooper());
134+
mainHandler.post(new Runnable() {
135+
@Override
136+
public void run() {
137+
try {
138+
Boolean isSpeechAvailable = SpeechRecognizer.isRecognitionAvailable(self.reactContext);
139+
callback.invoke(isSpeechAvailable, false);
140+
} catch(Exception e) {
141+
callback.invoke(false, e);
142+
}
143+
}
144+
});
145+
}
146+
147+
@ReactMethod
148+
public void isRecognizing(Callback callback) {
149+
final VoiceModule self = this;
150+
callback.invoke(isRecognizing);
151+
}
152+
153+
private void sendEvent(String eventName, @Nullable WritableMap params) {
154+
this.reactContext
155+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
156+
.emit(eventName, params);
157+
}
158+
159+
@Override
160+
public void onBeginningOfSpeech() {
161+
WritableMap event = Arguments.createMap();
162+
event.putBoolean("error", false);
163+
sendEvent("onSpeechStart", event);
164+
}
165+
166+
@Override
167+
public void onBufferReceived(byte[] buffer) {
168+
WritableMap event = Arguments.createMap();
169+
event.putBoolean("error", false);
170+
sendEvent("onSpeechRecognized", event);
171+
}
172+
173+
@Override
174+
public void onEndOfSpeech() {
175+
WritableMap event = Arguments.createMap();
176+
event.putBoolean("error", false);
177+
sendEvent("onSpeechEnd", event);
178+
}
179+
180+
@Override
181+
public void onError(int errorCode) {
182+
String errorMessage = getErrorText(errorCode);
183+
WritableMap event = Arguments.createMap();
184+
event.putString("error", errorMessage);
185+
sendEvent("onSpeechError", event);
186+
}
187+
188+
@Override
189+
public void onEvent(int arg0, Bundle arg1) { }
190+
191+
@Override
192+
public void onPartialResults(Bundle results) {
193+
WritableArray arr = Arguments.createArray();
194+
195+
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
196+
for (String result : matches) {
197+
arr.pushString(result);
198+
}
199+
200+
WritableMap event = Arguments.createMap();
201+
event.putArray("value", arr);
202+
sendEvent("onSpeechPartialResults", event);
203+
}
204+
205+
@Override
206+
public void onReadyForSpeech(Bundle arg0) {
207+
WritableMap event = Arguments.createMap();
208+
event.putBoolean("error", false);
209+
sendEvent("onSpeechStart", event);
210+
}
211+
212+
@Override
213+
public void onResults(Bundle results) {
214+
WritableArray arr = Arguments.createArray();
215+
216+
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
217+
for (String result : matches) {
218+
arr.pushString(result);
219+
}
220+
221+
WritableMap event = Arguments.createMap();
222+
event.putArray("value", arr);
223+
sendEvent("onSpeechResults", event);
224+
}
225+
226+
@Override
227+
public void onRmsChanged(float rmsdB) {
228+
WritableMap event = Arguments.createMap();
229+
event.putDouble("value", (double) rmsdB);
230+
sendEvent("onSpeechVolumeChanged", event);
231+
}
232+
233+
public static String getErrorText(int errorCode) {
234+
String message;
235+
switch (errorCode) {
236+
case SpeechRecognizer.ERROR_AUDIO:
237+
message = "Audio recording error";
238+
break;
239+
case SpeechRecognizer.ERROR_CLIENT:
240+
message = "Client side error";
241+
break;
242+
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
243+
message = "Insufficient permissions";
244+
break;
245+
case SpeechRecognizer.ERROR_NETWORK:
246+
message = "Network error";
247+
break;
248+
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
249+
message = "Network timeout";
250+
break;
251+
case SpeechRecognizer.ERROR_NO_MATCH:
252+
message = "No match";
253+
break;
254+
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
255+
message = "RecognitionService busy";
256+
break;
257+
case SpeechRecognizer.ERROR_SERVER:
258+
message = "error from server";
259+
break;
260+
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
261+
message = "No speech input";
262+
break;
263+
default:
264+
message = "Didn't understand, please try again.";
265+
break;
266+
}
267+
return message;
268+
}
269+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.wenkesj.voice;
2+
3+
import com.facebook.react.ReactPackage;
4+
import com.facebook.react.bridge.JavaScriptModule;
5+
import com.facebook.react.bridge.NativeModule;
6+
import com.facebook.react.bridge.ReactApplicationContext;
7+
import com.facebook.react.uimanager.ViewManager;
8+
9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.List;
12+
13+
public class VoicePackage implements ReactPackage {
14+
15+
@Override
16+
public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
17+
List<NativeModule> modules = new ArrayList<>();
18+
modules.add(new VoiceModule(reactApplicationContext));
19+
return modules;
20+
}
21+
22+
@Override
23+
public List<Class<? extends JavaScriptModule>> createJSModules() {
24+
return Collections.emptyList();
25+
}
26+
27+
@Override
28+
public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
29+
return Collections.emptyList();
30+
}
31+
}

‎index.js

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
'use strict';
2+
import React, {
3+
NativeModules,
4+
DeviceEventEmitter,
5+
} from 'react-native';
6+
7+
const { RCTVoice } = NativeModules;
8+
9+
class Voice {
10+
constructor() {
11+
this._loaded = false;
12+
this._events = {
13+
'onSpeechStart': this._onSpeechStart.bind(this),
14+
'onSpeechRecognized': this._onSpeechRecognized.bind(this),
15+
'onSpeechEnd': this._onSpeechEnd.bind(this),
16+
'onSpeechError': this._onSpeechError.bind(this),
17+
'onSpeechResults': this._onSpeechResults.bind(this),
18+
'onSpeechPartialResults': this._onSpeechPartialResults.bind(this),
19+
'onSpeechVolumeChanged': this._onSpeechVolumeChanged.bind(this)
20+
};
21+
}
22+
destroy() {
23+
return RCTVoice.destroySpeech((error) => {
24+
if (error) {
25+
return error;
26+
}
27+
Object.keys(this._events)
28+
.map((key, index) => DeviceEventEmitter.removeListener(key));
29+
return null;
30+
});
31+
}
32+
start() {
33+
if (!this._loaded) {
34+
Object.keys(this._events)
35+
.map((key, index) => DeviceEventEmitter.addListener(key, this._events[key]));
36+
}
37+
return RCTVoice.startSpeech((error) => {
38+
if (error) {
39+
return error;
40+
}
41+
return null;
42+
});
43+
}
44+
stop() {
45+
return RCTVoice.stopSpeech((error) => {
46+
if (error) {
47+
return error;
48+
}
49+
return null;
50+
});
51+
}
52+
cancel() {
53+
return RCTVoice.cancelSpeech((error) => {
54+
if (error) {
55+
return error;
56+
}
57+
return null;
58+
});
59+
}
60+
isAvailable(callback) {
61+
RCTVoice.isSpeechAvailable(callback);
62+
}
63+
isRecognizing() {
64+
return RCTVoice.isRecognizing(isRecognizing => isRecognizing);
65+
}
66+
_onSpeechStart(e) {
67+
if (this.onSpeechStart) {
68+
this.onSpeechStart(e);
69+
}
70+
}
71+
_onSpeechRecognized(e) {
72+
if (this.onSpeechRecognized) {
73+
this.onSpeechRecognized(e);
74+
}
75+
}
76+
_onSpeechEnd(e) {
77+
if (this.onSpeechEnd) {
78+
this.onSpeechEnd(e);
79+
}
80+
}
81+
_onSpeechError(e) {
82+
if (this.onSpeechError) {
83+
this.onSpeechError(e);
84+
}
85+
}
86+
_onSpeechResults(e) {
87+
if (this.onSpeechResults) {
88+
this.onSpeechResults(e);
89+
}
90+
}
91+
_onSpeechPartialResults(e) {
92+
if (this.onSpeechPartialResults) {
93+
this.onSpeechPartialResults(e);
94+
}
95+
}
96+
_onSpeechVolumeChanged(e) {
97+
if (this.onSpeechVolumeChanged) {
98+
this.onSpeechVolumeChanged(e);
99+
}
100+
}
101+
}
102+
103+
module.exports = new Voice();

‎package.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "react-native-voice",
3+
"version": "0.1.1",
4+
"description": "React Native Native Voice library for iOS and Android",
5+
"main": "index.js",
6+
"author": "Sam Wenke <samwenke@gmail.com>",
7+
"peerDependencies": {
8+
"react-native": "^0.14"
9+
},
10+
"keywords": [
11+
"react-native",
12+
"speech",
13+
"voice",
14+
"android",
15+
"ios"
16+
],
17+
"repository": {
18+
"type": "git",
19+
"url": "git://github.com/wenkesj/react-native-voice.git"
20+
},
21+
"license": "MIT"
22+
}

0 commit comments

Comments
 (0)
Please sign in to comment.