capacitor-microphone 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ package com.eddieagvictoria.capacitor_microphone;
2
+
3
+ import com.getcapacitor.JSObject;
4
+ import com.getcapacitor.Plugin;
5
+ import com.getcapacitor.PluginCall;
6
+ import com.getcapacitor.PluginMethod;
7
+ import com.getcapacitor.annotation.CapacitorPlugin;
8
+
9
+ import android.Manifest;
10
+ import com.getcapacitor.annotation.Permission;
11
+ import com.getcapacitor.annotation.PermissionCallback;
12
+ import com.getcapacitor.PermissionState;
13
+
14
+ import android.content.Intent;
15
+ import android.os.Bundle;
16
+ import android.speech.RecognitionListener;
17
+ import android.speech.RecognizerIntent;
18
+ import android.speech.SpeechRecognizer;
19
+
20
+ import java.util.ArrayList;
21
+
22
+ @CapacitorPlugin(
23
+ name = "CapacitorMicrophone",
24
+ permissions = {
25
+ @Permission(
26
+ alias = "microphone",
27
+ strings = { Manifest.permission.RECORD_AUDIO }
28
+ )
29
+ }
30
+ )
31
+ public class CapacitorMicrophonePlugin extends Plugin {
32
+
33
+ private SpeechRecognizer speechRecognizer;
34
+ private PluginCall currentCall;
35
+
36
+ @PluginMethod
37
+ public void checkPermission(PluginCall call) {
38
+ PermissionState state = getPermissionState("microphone");
39
+
40
+ JSObject result = new JSObject();
41
+ result.put("granted", state == PermissionState.GRANTED);
42
+ result.put("status",
43
+ state == PermissionState.GRANTED ? "granted" :
44
+ state == PermissionState.DENIED ? "denied" : "prompt"
45
+ );
46
+ result.put("details", "Android checkPermission");
47
+ result.put("errorMessage", "");
48
+
49
+ call.resolve(result);
50
+ }
51
+
52
+ @PluginMethod
53
+ public void requestPermission(PluginCall call) {
54
+ if (getPermissionState("microphone") == PermissionState.GRANTED) {
55
+ JSObject result = new JSObject();
56
+ result.put("granted", true);
57
+ result.put("status", "granted");
58
+ result.put("details", "Android permission already granted");
59
+ result.put("errorMessage", "");
60
+ call.resolve(result);
61
+ return;
62
+ }
63
+
64
+ requestPermissionForAlias("microphone", call, "permissionCallback");
65
+ }
66
+
67
+ @PluginMethod
68
+ public void checkRequestPermission(PluginCall call) {
69
+ PermissionState state = getPermissionState("microphone");
70
+
71
+ if (state == PermissionState.GRANTED) {
72
+ JSObject result = new JSObject();
73
+ result.put("granted", true);
74
+ result.put("status", "granted");
75
+ result.put("details", "Android microphone permission already granted");
76
+ result.put("errorMessage", "");
77
+ call.resolve(result);
78
+ return;
79
+ }
80
+
81
+ if (state == PermissionState.DENIED) {
82
+ requestPermissionForAlias("microphone", call, "permissionCallback");
83
+ return;
84
+ }
85
+
86
+ requestPermissionForAlias("microphone", call, "permissionCallback");
87
+ }
88
+
89
+ @PermissionCallback
90
+ private void permissionCallback(PluginCall call) {
91
+ boolean granted = getPermissionState("microphone") == PermissionState.GRANTED;
92
+
93
+ JSObject result = new JSObject();
94
+ result.put("granted", granted);
95
+ result.put("status", granted ? "granted" : "denied");
96
+ result.put("details", "Android permission callback");
97
+ result.put("errorMessage", granted ? "" : "User denied microphone permission");
98
+
99
+ call.resolve(result);
100
+ }
101
+
102
+
103
+
104
+ @PluginMethod
105
+ public void startListening(PluginCall call) {
106
+
107
+ if (speechRecognizer != null) {
108
+ call.reject("Speech recognition already running");
109
+ return;
110
+ }
111
+
112
+ if (getPermissionState("microphone") != PermissionState.GRANTED) {
113
+ call.reject("Microphone permission not granted");
114
+ return;
115
+ }
116
+
117
+ if (!SpeechRecognizer.isRecognitionAvailable(getContext())) {
118
+ call.reject("Speech recognition not available on this device");
119
+ return;
120
+ }
121
+
122
+ currentCall = call;
123
+
124
+ speechRecognizer = SpeechRecognizer.createSpeechRecognizer(getContext());
125
+
126
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
127
+ intent.putExtra(
128
+ RecognizerIntent.EXTRA_LANGUAGE_MODEL,
129
+ RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
130
+ );
131
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "es-MX");
132
+ intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
133
+
134
+ speechRecognizer.setRecognitionListener(new RecognitionListener() {
135
+
136
+ @Override
137
+ public void onResults(Bundle results) {
138
+ ArrayList<String> matches =
139
+ results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
140
+
141
+ JSObject ret = new JSObject();
142
+ ret.put("text", matches != null && !matches.isEmpty() ? matches.get(0) : "");
143
+ ret.put("isFinal", true);
144
+ if (currentCall != null) {
145
+ currentCall.resolve(ret);
146
+ currentCall = null;
147
+ }
148
+ }
149
+
150
+ @Override
151
+ public void onPartialResults(Bundle partialResults) {
152
+ ArrayList<String> matches =
153
+ partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
154
+
155
+ if (matches != null && !matches.isEmpty()) {
156
+ JSObject ret = new JSObject();
157
+ ret.put("text", matches.get(0));
158
+ ret.put("isFinal", false);
159
+
160
+ notifyListeners("partialResult", ret);
161
+ }
162
+ }
163
+
164
+ @Override public void onError(int error) {
165
+ if (currentCall != null) {
166
+ currentCall.reject(mapError(error));
167
+
168
+ currentCall = null;
169
+ }
170
+ }
171
+
172
+ // Métodos obligatorios (vacíos)
173
+ @Override public void onReadyForSpeech(Bundle params) {}
174
+ @Override public void onBeginningOfSpeech() {}
175
+ @Override public void onRmsChanged(float rmsdB) {}
176
+ @Override public void onBufferReceived(byte[] buffer) {}
177
+ @Override public void onEndOfSpeech() {}
178
+ @Override public void onEvent(int eventType, Bundle params) {}
179
+ });
180
+
181
+ speechRecognizer.startListening(intent);
182
+ }
183
+
184
+ @PluginMethod
185
+ public void stopListening(PluginCall call) {
186
+ if (speechRecognizer != null) {
187
+ speechRecognizer.stopListening();
188
+ speechRecognizer.destroy();
189
+ speechRecognizer = null;
190
+ }
191
+
192
+ JSObject ret = new JSObject();
193
+ ret.put("stopped", true);
194
+ call.resolve(ret);
195
+ }
196
+
197
+ @Override
198
+ protected void handleOnDestroy() {
199
+ if (speechRecognizer != null) {
200
+ speechRecognizer.destroy();
201
+ speechRecognizer = null;
202
+ }
203
+ }
204
+
205
+ private String mapError(int error) {
206
+ switch (error) {
207
+ case SpeechRecognizer.ERROR_AUDIO: return "Audio error";
208
+ case SpeechRecognizer.ERROR_NETWORK: return "Network error";
209
+ case SpeechRecognizer.ERROR_NO_MATCH: return "No speech recognized";
210
+ case SpeechRecognizer.ERROR_SPEECH_TIMEOUT: return "Speech timeout";
211
+ default: return "Unknown error";
212
+ }
213
+ }
214
+
215
+
216
+ }
217
+
218
+
219
+
@@ -0,0 +1,196 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import AVFoundation
4
+ import Speech
5
+
6
+
7
+ @objc(CapacitorMicrophonePlugin)
8
+ public class CapacitorMicrophonePlugin: CAPPlugin, CAPBridgedPlugin {
9
+
10
+ private let audioEngine = AVAudioEngine()
11
+ private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
12
+ private var recognitionTask: SFSpeechRecognitionTask?
13
+ private var speechRecognizer: SFSpeechRecognizer?
14
+ private var currentCall: CAPPluginCall?
15
+
16
+ public let identifier = "CapacitorMicrophonePlugin"
17
+ public let jsName = "CapacitorMicrophone"
18
+
19
+ public let pluginMethods: [CAPPluginMethod] = [
20
+ CAPPluginMethod(name: "checkPermission", returnType: CAPPluginReturnPromise),
21
+ CAPPluginMethod(name: "requestPermission", returnType: CAPPluginReturnPromise),
22
+ CAPPluginMethod(name: "checkRequestPermission", returnType: CAPPluginReturnPromise),
23
+ CAPPluginMethod(name: "startListening", returnType: CAPPluginReturnPromise),
24
+ CAPPluginMethod(name: "stopListening", returnType: CAPPluginReturnPromise)
25
+ ]
26
+
27
+ @objc func checkPermission(_ call: CAPPluginCall) {
28
+ let status = AVCaptureDevice.authorizationStatus(for: .audio)
29
+
30
+ var resultStatus = "prompt"
31
+ if status == .authorized {
32
+ resultStatus = "granted"
33
+ } else if status == .denied || status == .restricted {
34
+ resultStatus = "denied"
35
+ }
36
+
37
+ call.resolve([
38
+ "granted": resultStatus == "granted",
39
+ "status": resultStatus,
40
+ "details": "iOS checkPermission",
41
+ "errorMessage": ""
42
+ ])
43
+ }
44
+
45
+ @objc func requestPermission(_ call: CAPPluginCall) {
46
+ AVCaptureDevice.requestAccess(for: .audio) { granted in
47
+ call.resolve([
48
+ "granted": granted,
49
+ "status": granted ? "granted" : "denied",
50
+ "details": "iOS requestPermission",
51
+ "errorMessage": granted ? "" : "User denied microphone permission"
52
+ ])
53
+ }
54
+ }
55
+
56
+ @objc func checkRequestPermission(_ call: CAPPluginCall) {
57
+ let status = AVCaptureDevice.authorizationStatus(for: .audio)
58
+
59
+ if status == .authorized {
60
+ call.resolve([
61
+ "granted": true,
62
+ "status": "granted",
63
+ "details": "iOS already authorized",
64
+ "errorMessage": ""
65
+ ])
66
+ } else if status == .notDetermined {
67
+ AVCaptureDevice.requestAccess(for: .audio) { granted in
68
+ call.resolve([
69
+ "granted": granted,
70
+ "status": granted ? "granted" : "denied",
71
+ "details": "iOS requested permission",
72
+ "errorMessage": granted ? "" : "User denied microphone permission"
73
+ ])
74
+ }
75
+ } else {
76
+ call.resolve([
77
+ "granted": false,
78
+ "status": "denied",
79
+ "details": "iOS permission denied or restricted",
80
+ "errorMessage": "Microphone permission denied"
81
+ ])
82
+ }
83
+ }
84
+
85
+ @objc func startListening(_ call: CAPPluginCall) {
86
+
87
+ if audioEngine.isRunning {
88
+ call.reject("Speech recognition already running")
89
+ return
90
+ }
91
+
92
+ self.currentCall = call
93
+
94
+ let lang = call.getString("lang") ?? "es-MX"
95
+ speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: lang))
96
+
97
+ SFSpeechRecognizer.requestAuthorization { authStatus in
98
+ if authStatus != .authorized {
99
+ self.currentCall?.reject("Speech recognition not authorized")
100
+ self.currentCall = nil
101
+ return
102
+ }
103
+
104
+ DispatchQueue.main.async {
105
+ self.startRecognition()
106
+ }
107
+ }
108
+
109
+ }
110
+
111
+ private func startRecognition() {
112
+
113
+ recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
114
+ guard let recognitionRequest = recognitionRequest else {
115
+ self.currentCall?.reject("Unable to create recognition request")
116
+ self.currentCall = nil
117
+ return
118
+ }
119
+
120
+ recognitionRequest.shouldReportPartialResults = true
121
+
122
+ let inputNode = audioEngine.inputNode
123
+
124
+ recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest) { result, error in
125
+ if let result = result {
126
+
127
+ self.notifyListeners("partialResult", data: [
128
+ "text": result.bestTranscription.formattedString,
129
+ "isFinal": result.isFinal
130
+ ])
131
+
132
+ if result.isFinal, let currentCall = self.currentCall {
133
+ currentCall.resolve([
134
+ "text": result.bestTranscription.formattedString,
135
+ "isFinal": true
136
+ ])
137
+ self.currentCall = nil
138
+ self.stopAudio()
139
+ }
140
+ }
141
+
142
+ if let error = error, let currentCall = self.currentCall {
143
+ currentCall.reject(error.localizedDescription)
144
+ self.currentCall = nil
145
+ self.stopAudio()
146
+ }
147
+ }
148
+
149
+ let recordingFormat = inputNode.outputFormat(forBus: 0)
150
+ inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) {
151
+ buffer, _ in
152
+ recognitionRequest.append(buffer)
153
+ }
154
+
155
+ audioEngine.prepare()
156
+
157
+ do {
158
+ let audioSession = AVAudioSession.sharedInstance()
159
+ try audioSession.setCategory(.record, mode: .measurement, options: .duckOthers)
160
+ try audioSession.setActive(true, options: .notifyOthersOnDeactivation)
161
+ try audioEngine.start()
162
+ } catch {
163
+ if let currentCall = self.currentCall {
164
+ currentCall.reject("Audio engine could not start")
165
+ self.currentCall = nil
166
+ }
167
+ }
168
+ }
169
+
170
+
171
+ @objc func stopListening(_ call: CAPPluginCall) {
172
+ stopAudio()
173
+ call.resolve(["stopped": true])
174
+ }
175
+
176
+ private func stopAudio() {
177
+ if audioEngine.isRunning {
178
+ audioEngine.stop()
179
+ }
180
+ if audioEngine.inputNode.numberOfInputs > 0 {
181
+ audioEngine.inputNode.removeTap(onBus: 0)
182
+ }
183
+ recognitionRequest?.endAudio()
184
+
185
+ recognitionTask?.cancel()
186
+ recognitionTask = nil
187
+ recognitionRequest = nil
188
+ }
189
+
190
+ override public func handleOnDestroy() {
191
+ stopAudio()
192
+ }
193
+
194
+
195
+
196
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-microphone",
3
- "version": "0.0.2",
3
+ "version": "0.0.5",
4
4
  "description": "plugin to use the microphone",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -1,91 +0,0 @@
1
- package com.eddieagvictoria.capacitor_microphone;
2
-
3
- import com.getcapacitor.JSObject;
4
- import com.getcapacitor.Plugin;
5
- import com.getcapacitor.PluginCall;
6
- import com.getcapacitor.PluginMethod;
7
- import com.getcapacitor.annotation.CapacitorPlugin;
8
-
9
- import android.Manifest;
10
- import com.getcapacitor.Permission;
11
- import com.getcapacitor.PermissionState;
12
-
13
- @CapacitorPlugin(
14
- name = "CapacitorMicrophone",
15
- permissions = {
16
- @Permission(
17
- alias = "microphone",
18
- strings = { Manifest.permission.RECORD_AUDIO }
19
- )
20
- }
21
- )
22
- public class CapacitorMicrophonePlugin extends Plugin {
23
-
24
- @PluginMethod
25
- public void checkPermission(PluginCall call) {
26
- PermissionState state = getPermissionState("microphone");
27
-
28
- JSObject result = new JSObject();
29
- result.put("granted", state == PermissionState.GRANTED);
30
- result.put("status",
31
- state == PermissionState.GRANTED ? "granted" :
32
- state == PermissionState.DENIED ? "denied" : "prompt"
33
- );
34
- result.put("details", "Android checkPermission");
35
- result.put("errorMessage", "");
36
-
37
- call.resolve(result);
38
- }
39
-
40
- @PluginMethod
41
- public void requestPermission(PluginCall call) {
42
- if (getPermissionState("microphone") == PermissionState.GRANTED) {
43
- JSObject result = new JSObject();
44
- result.put("granted", true);
45
- result.put("status", "granted");
46
- result.put("details", "Android permission already granted");
47
- result.put("errorMessage", "");
48
- call.resolve(result);
49
- return;
50
- }
51
-
52
- requestPermissionForAlias("microphone", call, "permissionCallback");
53
- }
54
-
55
- @PluginMethod
56
- public void checkRequestPermission(PluginCall call) {
57
- PermissionState state = getPermissionState("microphone");
58
-
59
- if (state == PermissionState.GRANTED) {
60
- JSObject result = new JSObject();
61
- result.put("granted", true);
62
- result.put("status", "granted");
63
- result.put("details", "Android microphone permission already granted");
64
- result.put("errorMessage", "");
65
- call.resolve(result);
66
- return;
67
- }
68
-
69
- if (state == PermissionState.DENIED) {
70
- requestPermissionForAlias("microphone", call, "permissionCallback");
71
- return;
72
- }
73
-
74
- requestPermissionForAlias("microphone", call, "permissionCallback");
75
- }
76
-
77
- @PermissionCallback
78
- private void permissionCallback(PluginCall call) {
79
- boolean granted = getPermissionState("microphone") == PermissionState.GRANTED;
80
-
81
- JSObject result = new JSObject();
82
- result.put("granted", granted);
83
- result.put("status", granted ? "granted" : "denied");
84
- result.put("details", "Android permission callback");
85
- result.put("errorMessage", granted ? "" : "User denied microphone permission");
86
-
87
- call.resolve(result);
88
- }
89
-
90
-
91
- }
@@ -1,74 +0,0 @@
1
- import Foundation
2
- import Capacitor
3
- import AVFoundation
4
-
5
- @objc(CapacitorMicrophonePlugin)
6
- public class CapacitorMicrophonePlugin: CAPPlugin, CAPBridgedPlugin {
7
-
8
- public let identifier = "CapacitorMicrophonePlugin"
9
- public let jsName = "CapacitorMicrophone"
10
-
11
- public let pluginMethods: [CAPPluginMethod] = [
12
- CAPPluginMethod(name: "checkPermission", returnType: CAPPluginReturnPromise),
13
- CAPPluginMethod(name: "requestPermission", returnType: CAPPluginReturnPromise),
14
- CAPPluginMethod(name: "checkRequestPermission", returnType: CAPPluginReturnPromise)
15
- ]
16
-
17
- @objc func checkPermission(_ call: CAPPluginCall) {
18
- let status = AVCaptureDevice.authorizationStatus(for: .audio)
19
-
20
- var resultStatus = "prompt"
21
- if status == .authorized {
22
- resultStatus = "granted"
23
- } else if status == .denied || status == .restricted {
24
- resultStatus = "denied"
25
- }
26
-
27
- call.resolve([
28
- "granted": resultStatus == "granted",
29
- "status": resultStatus,
30
- "details": "iOS checkPermission",
31
- "errorMessage": ""
32
- ])
33
- }
34
-
35
- @objc func requestPermission(_ call: CAPPluginCall) {
36
- AVCaptureDevice.requestAccess(for: .audio) { granted in
37
- call.resolve([
38
- "granted": granted,
39
- "status": granted ? "granted" : "denied",
40
- "details": "iOS requestPermission",
41
- "errorMessage": granted ? "" : "User denied microphone permission"
42
- ])
43
- }
44
- }
45
-
46
- @objc func checkRequestPermission(_ call: CAPPluginCall) {
47
- let status = AVCaptureDevice.authorizationStatus(for: .audio)
48
-
49
- if status == .authorized {
50
- call.resolve([
51
- "granted": true,
52
- "status": "granted",
53
- "details": "iOS already authorized",
54
- "errorMessage": ""
55
- ])
56
- } else if status == .notDetermined {
57
- AVCaptureDevice.requestAccess(for: .audio) { granted in
58
- call.resolve([
59
- "granted": granted,
60
- "status": granted ? "granted" : "denied",
61
- "details": "iOS requested permission",
62
- "errorMessage": granted ? "" : "User denied microphone permission"
63
- ])
64
- }
65
- } else {
66
- call.resolve([
67
- "granted": false,
68
- "status": "denied",
69
- "details": "iOS permission denied or restricted",
70
- "errorMessage": "Microphone permission denied"
71
- ])
72
- }
73
- }
74
- }