├── .eslintignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── lock.yml │ ├── needs-reply-remove.yml │ ├── needs-reply.yml │ └── needs-triage.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CapacitorCommunitySpeechRecognition.podspec ├── LICENSE ├── README.md ├── android ├── .npmignore ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── proguard-rules.pro ├── settings.gradle └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── getcapacitor │ │ └── android │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── getcapacitor │ │ │ └── community │ │ │ └── speechrecognition │ │ │ ├── Constants.java │ │ │ ├── Receiver.java │ │ │ └── SpeechRecognition.java │ └── res │ │ ├── layout │ │ └── bridge_layout_main.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── getcapacitor │ └── ExampleUnitTest.java ├── ios ├── Plugin.xcodeproj │ └── project.pbxproj ├── Plugin.xcworkspace │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Plugin │ ├── Info.plist │ ├── Plugin.h │ ├── Plugin.m │ └── Plugin.swift ├── PluginTests │ ├── Info.plist │ └── PluginTests.swift └── Podfile ├── package-lock.json ├── package.json ├── rollup.config.mjs ├── src ├── definitions.ts ├── index.ts └── web.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "bug: " 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Plugin version:** 10 | 11 | 12 | 13 | 14 | **Platform(s):** 15 | 16 | 17 | 18 | 19 | **Current behavior:** 20 | 21 | 22 | 23 | 24 | **Expected behavior:** 25 | 26 | 27 | 28 | 29 | **Steps to reproduce:** 30 | 31 | 32 | 33 | 34 | **Related code:** 35 | 36 | 37 | ``` 38 | insert short code snippets here 39 | ``` 40 | 41 | **Other information:** 42 | 43 | 44 | 45 | 46 | **Capacitor doctor:** 47 | 48 | 49 | ``` 50 | insert the output from `npx cap doctor` here 51 | ``` 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "feat: " 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe:** 10 | 11 | 12 | 13 | 14 | **Describe the solution you'd like:** 15 | 16 | 17 | 18 | 19 | **Describe alternatives you've considered:** 20 | 21 | 22 | 23 | 24 | **Additional context:** 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Pull request checklist 2 | 3 | Please check if your PR fulfills the following requirements: 4 | 5 | - [ ] The changes have been tested successfully. 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths-ignore: 8 | - "**.md" 9 | - ".vscode/**" 10 | pull_request: 11 | paths-ignore: 12 | - "**.md" 13 | - ".vscode/**" 14 | workflow_dispatch: 15 | 16 | env: 17 | NODE_VERSION: 18 18 | JAVA_VERSION: 21 19 | 20 | jobs: 21 | build: 22 | name: Build 23 | runs-on: macos-14 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | - name: Set up Node.js ${{ env.NODE_VERSION }} 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: ${{ env.NODE_VERSION }} 31 | - name: Set up Java ${{ env.JAVA_VERSION }} 32 | uses: actions/setup-java@v4 33 | with: 34 | distribution: 'zulu' 35 | java-version: ${{ env.JAVA_VERSION }} 36 | - name: Install dependencies 37 | run: npm ci 38 | - name: Build iOS 39 | run: npm run verify:ios 40 | - name: Build Android 41 | run: npm run verify:android 42 | - name: Build Web 43 | run: npm run verify:web 44 | - name: Upload artifacts 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: dist 48 | path: dist 49 | lint: 50 | name: Lint 51 | runs-on: macos-latest 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v4 55 | - name: Set up Node.js ${{ env.NODE_VERSION }} 56 | uses: actions/setup-node@v4 57 | with: 58 | node-version: ${{ env.NODE_VERSION }} 59 | - name: Install dependencies 60 | run: npm ci 61 | - name: Run Lint script 62 | run: npm run lint 63 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: Lock old issues and pull requests that are closed 2 | 3 | on: 4 | schedule: 5 | - cron: '0 18 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | concurrency: 13 | group: lock 14 | 15 | jobs: 16 | action: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: dessant/lock-threads@v3 20 | with: 21 | issue-inactive-days: '56' 22 | -------------------------------------------------------------------------------- /.github/workflows/needs-reply-remove.yml: -------------------------------------------------------------------------------- 1 | name: Remove needs-reply label 2 | 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | 8 | jobs: 9 | needs-reply: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Remove needs-reply label 13 | run: | 14 | curl --request DELETE \ 15 | --url 'https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/labels/needs%3A%20reply' \ 16 | --header 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' 17 | -------------------------------------------------------------------------------- /.github/workflows/needs-reply.yml: -------------------------------------------------------------------------------- 1 | name: Close old issues that need reply 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | needs-reply: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Close old issues that need reply 13 | uses: dwieeb/needs-reply@v2 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | issue-label: 'needs: reply' 17 | -------------------------------------------------------------------------------- /.github/workflows/needs-triage.yml: -------------------------------------------------------------------------------- 1 | name: Add needs-triage label 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | needs-triage: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Add needs-triage label 13 | if: join(github.event.issue.labels) == '' 14 | run: | 15 | curl --request POST \ 16 | --url 'https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/labels' \ 17 | --header 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' \ 18 | --header 'Content-Type: application/json' \ 19 | --header 'Accept: application/vnd.github.v3+json' \ 20 | --data-raw '{ "labels": ["needs: triage"] }' 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # node files 2 | dist 3 | node_modules 4 | 5 | # iOS files 6 | Pods 7 | Podfile.lock 8 | Build 9 | xcuserdata 10 | 11 | # macOS files 12 | .DS_Store 13 | 14 | 15 | 16 | # Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore 17 | 18 | # Built application files 19 | *.apk 20 | *.ap_ 21 | 22 | # Files for the ART/Dalvik VM 23 | *.dex 24 | 25 | # Java class files 26 | *.class 27 | 28 | # Generated files 29 | bin 30 | gen 31 | out 32 | 33 | # Gradle files 34 | .gradle 35 | build 36 | 37 | # Local configuration file (sdk path, etc) 38 | local.properties 39 | 40 | # Proguard folder generated by Eclipse 41 | proguard 42 | 43 | # Log Files 44 | *.log 45 | 46 | # Android Studio Navigation editor temp files 47 | .navigation 48 | 49 | # Android Studio captures folder 50 | captures 51 | 52 | # IntelliJ 53 | *.iml 54 | .idea 55 | 56 | # Keystore files 57 | # Uncomment the following line if you do not want to check your keystore files in. 58 | #*.jks 59 | 60 | # External native build folder generated in Android Studio 2.2 and later 61 | .externalNativeBuild 62 | 63 | # VSCode 64 | .vscode/settings.json 65 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [7.0.1](https://github.com/capacitor-community/speech-recognition/compare/v7.0.0...v7.0.1) (2025-06-09) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * gracefully handle AVAudioSession conflict when mic is busy ([#116](https://github.com/capacitor-community/speech-recognition/issues/116)) ([36cc033](https://github.com/capacitor-community/speech-recognition/commit/36cc0333c69ab676eb057f81cec2e9be22398cc9)) 11 | 12 | ## [7.0.0](https://github.com/capacitor-community/speech-recognition/compare/v6.0.1...v7.0.0) (2025-03-25) 13 | 14 | 15 | ### ⚠ BREAKING CHANGES 16 | 17 | * This plugin now only supports Capacitor 7. 18 | 19 | ### Features 20 | 21 | * update to Capacitor 7 ([#114](https://github.com/capacitor-community/speech-recognition/issues/114)) ([bb13bee](https://github.com/capacitor-community/speech-recognition/commit/bb13beec20410b6d3b2e4192cf9fa14cc55c3360)) 22 | 23 | ### [6.0.1](https://github.com/capacitor-community/speech-recognition/compare/v6.0.0...v6.0.1) (2024-06-28) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * **ios:** proper isListening return ([#103](https://github.com/capacitor-community/speech-recognition/issues/103)) ([daf40a7](https://github.com/capacitor-community/speech-recognition/commit/daf40a73fda9b1caa5e84df41bf15ff687617742)) 29 | 30 | ## [6.0.0](https://github.com/capacitor-community/speech-recognition/compare/v5.1.0...v6.0.0) (2024-06-07) 31 | 32 | 33 | ### ⚠ BREAKING CHANGES 34 | 35 | * Remove deprecated permission methods (#94) 36 | * Update to Capacitor 6 (#86) 37 | * use correct name on rollup (#93) 38 | 39 | ### Features 40 | 41 | * Remove deprecated permission methods ([#94](https://github.com/capacitor-community/speech-recognition/issues/94)) ([77d89b8](https://github.com/capacitor-community/speech-recognition/commit/77d89b86117a9e1adc88abfafc8c9327ea5fef8d)) 42 | * Update to Capacitor 6 ([#86](https://github.com/capacitor-community/speech-recognition/issues/86)) ([8e2f62b](https://github.com/capacitor-community/speech-recognition/commit/8e2f62b5ed37fdb8acf33c31b4e7157d03a47739)) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * correct since for isListening and listeningState ([#92](https://github.com/capacitor-community/speech-recognition/issues/92)) ([8b9445c](https://github.com/capacitor-community/speech-recognition/commit/8b9445caf09093422d761c6b3f91ed330d273047)) 48 | * **ios:** add missing listeningState stopped cases ([#95](https://github.com/capacitor-community/speech-recognition/issues/95)) ([19f98d1](https://github.com/capacitor-community/speech-recognition/commit/19f98d13b6a9454373a7d1af57e83f49fa823174)) 49 | * use correct name on rollup ([#93](https://github.com/capacitor-community/speech-recognition/issues/93)) ([d8fefbc](https://github.com/capacitor-community/speech-recognition/commit/d8fefbc13594c7949e3bc687355c7308d9f90d8d)) 50 | 51 | ## [5.1.0](https://github.com/capacitor-community/speech-recognition/compare/v5.0.0...v5.1.0) (2024-03-27) 52 | 53 | 54 | ### Features 55 | 56 | * add `isListening` method and `listeningState` listener ([#83](https://github.com/capacitor-community/speech-recognition/issues/83)) ([5a9b532](https://github.com/capacitor-community/speech-recognition/commit/5a9b532f316df7585b94e65bff77b642df5eb32e)) 57 | 58 | # Changelog 59 | 60 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 61 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This guide provides instructions for contributing to this Capacitor plugin. 4 | 5 | ## Developing 6 | 7 | ### Local Setup 8 | 9 | 1. Fork and clone the repo. 10 | 2. Install the dependencies. 11 | 12 | ```shell 13 | npm install 14 | ``` 15 | 16 | 3. Install SwiftLint if you're on macOS. 17 | 18 | ```shell 19 | brew install swiftlint 20 | ``` 21 | 22 | ### Scripts 23 | 24 | #### `npm run build` 25 | 26 | Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen). 27 | 28 | It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported. 29 | 30 | Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`. 31 | 32 | #### `npm run verify` 33 | 34 | Build and validate the web and native projects. 35 | 36 | This is useful to run in CI to verify that the plugin builds for all platforms. 37 | 38 | #### `npm run lint` / `npm run fmt` 39 | 40 | Check formatting and code quality, autoformat/autofix if possible. 41 | 42 | This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation. 43 | 44 | ## Versioning 45 | 46 | Don't change the plugin version manually, the version change is automated with `standard-version` package. 47 | 48 | ## Publishing 49 | 50 | First run: 51 | 52 | ```shell 53 | npm run release 54 | ``` 55 | 56 | That will update the plugin version and update the `CHANGELOG.md` file with latest changes. Then it will ask you to run: 57 | 58 | ```shell 59 | git push --follow-tags origin master && npm publish 60 | ``` 61 | 62 | That creates a tag on gitbhub and publishes the package on npm. 63 | 64 | Go to the [github tags section](https://github.com/capacitor-community/speech-recognition/tags), pick the latest tag and create a release for it. 65 | 66 | > **Note**: The [`files`](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#files) array in `package.json` specifies which files get published. If you rename files/directories or add files elsewhere, you may need to update it. 67 | -------------------------------------------------------------------------------- /CapacitorCommunitySpeechRecognition.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'CapacitorCommunitySpeechRecognition' 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.license = package['license'] 10 | s.homepage = package['repository']['url'] 11 | s.author = package['author'] 12 | s.source = { :git => package['repository']['url'], :tag => s.version.to_s } 13 | s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' 14 | s.ios.deployment_target = '14.0' 15 | s.dependency 'Capacitor' 16 | s.swift_version = '5.1' 17 | end 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) COPYRIGHT_YEAR COPYRIGHT_HOLDER 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Capacitor Speech Recognition Plugin 2 | 3 | Capacitor community plugin for speech recognition. 4 | 5 | ## Maintainers 6 | 7 | | Maintainer | GitHub | Social | 8 | | --------------- | ------------------------------------------- | ------------------------------------------------ | 9 | | Priyank Patel | [priyankpat](https://github.com/priyankpat) | [@priyankpat\_](https://twitter.com/priyankpat_) | 10 | | Matteo Padovano | [mrbatista](https://github.com/mrbatista) | [@mrba7ista](https://twitter.com/mrba7ista) | 11 | 12 | Maintenance Status: Actively Maintained 13 | 14 | ## Installation 15 | 16 | To use npm 17 | 18 | ```bash 19 | npm install @capacitor-community/speech-recognition 20 | ``` 21 | 22 | To use yarn 23 | 24 | ```bash 25 | yarn add @capacitor-community/speech-recognition 26 | ``` 27 | 28 | Sync native files 29 | 30 | ```bash 31 | npx cap sync 32 | ``` 33 | 34 | ## iOS 35 | 36 | iOS requires the following usage descriptions be added and filled out for your app in `Info.plist`: 37 | 38 | - `NSSpeechRecognitionUsageDescription` (`Privacy - Speech Recognition Usage Description`) 39 | - `NSMicrophoneUsageDescription` (`Privacy - Microphone Usage Description`) 40 | 41 | ## Android 42 | 43 | No further action required. 44 | 45 | ## Supported methods 46 | 47 | 48 | 49 | * [`available()`](#available) 50 | * [`start(...)`](#start) 51 | * [`stop()`](#stop) 52 | * [`getSupportedLanguages()`](#getsupportedlanguages) 53 | * [`isListening()`](#islistening) 54 | * [`checkPermissions()`](#checkpermissions) 55 | * [`requestPermissions()`](#requestpermissions) 56 | * [`addListener('partialResults', ...)`](#addlistenerpartialresults-) 57 | * [`addListener('listeningState', ...)`](#addlistenerlisteningstate-) 58 | * [`removeAllListeners()`](#removealllisteners) 59 | * [Interfaces](#interfaces) 60 | * [Type Aliases](#type-aliases) 61 | 62 | 63 | 64 | ## Example 65 | 66 | ```typescript 67 | import { SpeechRecognition } from "@capacitor-community/speech-recognition"; 68 | 69 | SpeechRecognition.available(); 70 | 71 | SpeechRecognition.start({ 72 | language: "en-US", 73 | maxResults: 2, 74 | prompt: "Say something", 75 | partialResults: true, 76 | popup: true, 77 | }); 78 | // listen to partial results 79 | SpeechRecognition.addListener("partialResults", (data: any) => { 80 | console.log("partialResults was fired", data.matches); 81 | }); 82 | 83 | // stop listening partial results 84 | SpeechRecognition.removeAllListeners(); 85 | 86 | SpeechRecognition.stop(); 87 | 88 | SpeechRecognition.getSupportedLanguages(); 89 | 90 | SpeechRecognition.checkPermissions(); 91 | 92 | SpeechRecognition.requestPermissions(); 93 | 94 | SpeechRecognition.hasPermission(); 95 | 96 | SpeechRecognition.requestPermission(); 97 | ``` 98 | 99 | 100 | 101 | 102 | ### available() 103 | 104 | ```typescript 105 | available() => Promise<{ available: boolean; }> 106 | ``` 107 | 108 | This method will check if speech recognition feature is available on the device. 109 | 110 | **Returns:** Promise<{ available: boolean; }> 111 | 112 | -------------------- 113 | 114 | 115 | ### start(...) 116 | 117 | ```typescript 118 | start(options?: UtteranceOptions | undefined) => Promise<{ matches?: string[]; }> 119 | ``` 120 | 121 | This method will start to listen for utterance. 122 | 123 | if `partialResults` is `true`, the function respond directly without result and 124 | event `partialResults` will be emit for each partial result, until stopped. 125 | 126 | | Param | Type | 127 | | ------------- | ------------------------------------------------------------- | 128 | | **`options`** | UtteranceOptions | 129 | 130 | **Returns:** Promise<{ matches?: string[]; }> 131 | 132 | -------------------- 133 | 134 | 135 | ### stop() 136 | 137 | ```typescript 138 | stop() => Promise 139 | ``` 140 | 141 | This method will stop listening for utterance 142 | 143 | -------------------- 144 | 145 | 146 | ### getSupportedLanguages() 147 | 148 | ```typescript 149 | getSupportedLanguages() => Promise<{ languages: any[]; }> 150 | ``` 151 | 152 | This method will return list of languages supported by the speech recognizer. 153 | 154 | It's not available on Android 13 and newer. 155 | 156 | **Returns:** Promise<{ languages: any[]; }> 157 | 158 | -------------------- 159 | 160 | 161 | ### isListening() 162 | 163 | ```typescript 164 | isListening() => Promise<{ listening: boolean; }> 165 | ``` 166 | 167 | This method will check if speech recognition is listening. 168 | 169 | **Returns:** Promise<{ listening: boolean; }> 170 | 171 | **Since:** 5.1.0 172 | 173 | -------------------- 174 | 175 | 176 | ### checkPermissions() 177 | 178 | ```typescript 179 | checkPermissions() => Promise 180 | ``` 181 | 182 | Check the speech recognition permission. 183 | 184 | **Returns:** Promise<PermissionStatus> 185 | 186 | **Since:** 5.0.0 187 | 188 | -------------------- 189 | 190 | 191 | ### requestPermissions() 192 | 193 | ```typescript 194 | requestPermissions() => Promise 195 | ``` 196 | 197 | Request the speech recognition permission. 198 | 199 | **Returns:** Promise<PermissionStatus> 200 | 201 | **Since:** 5.0.0 202 | 203 | -------------------- 204 | 205 | 206 | ### addListener('partialResults', ...) 207 | 208 | ```typescript 209 | addListener(eventName: 'partialResults', listenerFunc: (data: { matches: string[]; }) => void) => Promise 210 | ``` 211 | 212 | Called when partialResults set to true and result received. 213 | 214 | On Android it doesn't work if popup is true. 215 | 216 | Provides partial result. 217 | 218 | | Param | Type | 219 | | ------------------ | ------------------------------------------------------ | 220 | | **`eventName`** | 'partialResults' | 221 | | **`listenerFunc`** | (data: { matches: string[]; }) => void | 222 | 223 | **Returns:** Promise<PluginListenerHandle> 224 | 225 | **Since:** 2.0.2 226 | 227 | -------------------- 228 | 229 | 230 | ### addListener('listeningState', ...) 231 | 232 | ```typescript 233 | addListener(eventName: 'listeningState', listenerFunc: (data: { status: 'started' | 'stopped'; }) => void) => Promise 234 | ``` 235 | 236 | Called when listening state changed. 237 | 238 | | Param | Type | 239 | | ------------------ | ------------------------------------------------------------------- | 240 | | **`eventName`** | 'listeningState' | 241 | | **`listenerFunc`** | (data: { status: 'started' \| 'stopped'; }) => void | 242 | 243 | **Returns:** Promise<PluginListenerHandle> 244 | 245 | **Since:** 5.1.0 246 | 247 | -------------------- 248 | 249 | 250 | ### removeAllListeners() 251 | 252 | ```typescript 253 | removeAllListeners() => Promise 254 | ``` 255 | 256 | Remove all the listeners that are attached to this plugin. 257 | 258 | **Since:** 4.0.0 259 | 260 | -------------------- 261 | 262 | 263 | ### Interfaces 264 | 265 | 266 | #### UtteranceOptions 267 | 268 | | Prop | Type | Description | 269 | | -------------------- | -------------------- | ---------------------------------------------------------------- | 270 | | **`language`** | string | key returned from `getSupportedLanguages()` | 271 | | **`maxResults`** | number | maximum number of results to return (5 is max) | 272 | | **`prompt`** | string | prompt message to display on popup (Android only) | 273 | | **`popup`** | boolean | display popup window when listening for utterance (Android only) | 274 | | **`partialResults`** | boolean | return partial results if found | 275 | 276 | 277 | #### PermissionStatus 278 | 279 | | Prop | Type | Description | Since | 280 | | ----------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | 281 | | **`speechRecognition`** | PermissionState | Permission state for speechRecognition alias. On Android it requests/checks RECORD_AUDIO permission On iOS it requests/checks the speech recognition and microphone permissions. | 5.0.0 | 282 | 283 | 284 | #### PluginListenerHandle 285 | 286 | | Prop | Type | 287 | | ------------ | ----------------------------------------- | 288 | | **`remove`** | () => Promise<void> | 289 | 290 | 291 | ### Type Aliases 292 | 293 | 294 | #### PermissionState 295 | 296 | 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' 297 | 298 | 299 | -------------------------------------------------------------------------------- /android/.npmignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2' 3 | androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0' 4 | androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1' 5 | androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1' 6 | } 7 | 8 | buildscript { 9 | repositories { 10 | google() 11 | mavenCentral() 12 | } 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:8.7.2' 15 | } 16 | } 17 | 18 | apply plugin: 'com.android.library' 19 | 20 | android { 21 | namespace "com.getcapacitor.community.speechrecognition.speechrecognition" 22 | compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35 23 | defaultConfig { 24 | minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23 25 | targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35 26 | versionCode 1 27 | versionName '1.0' 28 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 29 | } 30 | buildTypes { 31 | release { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | } 35 | } 36 | lintOptions { 37 | abortOnError false 38 | } 39 | compileOptions { 40 | sourceCompatibility JavaVersion.VERSION_21 41 | targetCompatibility JavaVersion.VERSION_21 42 | } 43 | } 44 | 45 | repositories { 46 | google() 47 | mavenCentral() 48 | } 49 | 50 | dependencies { 51 | implementation fileTree(dir: 'libs', include: ['*.jar']) 52 | implementation project(':capacitor-android') 53 | implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" 54 | testImplementation "junit:junit:$junitVersion" 55 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" 56 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" 57 | } 58 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | # Supports AndroidX 20 | android.useAndroidX=true 21 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capacitor-community/speech-recognition/498ed497cf49459dd6badb4b366b61b250333252/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':capacitor-android' 2 | project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') -------------------------------------------------------------------------------- /android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.android; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import android.content.Context; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | import androidx.test.platform.app.InstrumentationRegistry; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see Testing documentation 15 | */ 16 | @RunWith(AndroidJUnit4.class) 17 | public class ExampleInstrumentedTest { 18 | 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 23 | 24 | assertEquals("com.getcapacitor.android", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/community/speechrecognition/Constants.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.community.speechrecognition; 2 | 3 | import android.Manifest; 4 | 5 | public interface Constants { 6 | int REQUEST_CODE_PERMISSION = 2001; 7 | int REQUEST_CODE_SPEECH = 2002; 8 | String IS_RECOGNITION_AVAILABLE = "isRecognitionAvailable"; 9 | String START_LISTENING = "startListening"; 10 | String STOP_LISTENING = "stopListening"; 11 | String GET_SUPPORTED_LANGUAGES = "getSupportedLanguages"; 12 | String HAS_PERMISSION = "hasPermission"; 13 | String REQUEST_PERMISSION = "requestPermission"; 14 | int MAX_RESULTS = 5; 15 | String NOT_AVAILABLE = "Speech recognition service is not available."; 16 | String MISSING_PERMISSION = "Missing permission"; 17 | String RECORD_AUDIO_PERMISSION = Manifest.permission.RECORD_AUDIO; 18 | String ERROR = "Could not get list of languages"; 19 | } 20 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/community/speechrecognition/Receiver.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.community.speechrecognition; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.speech.RecognizerIntent; 8 | import com.getcapacitor.JSArray; 9 | import com.getcapacitor.JSObject; 10 | import com.getcapacitor.PluginCall; 11 | import java.util.List; 12 | 13 | public class Receiver extends BroadcastReceiver implements Constants { 14 | 15 | public static final String TAG = "Receiver"; 16 | 17 | private List supportedLanguagesList; 18 | private String languagePref; 19 | private PluginCall call; 20 | 21 | public Receiver(PluginCall call) { 22 | super(); 23 | this.call = call; 24 | } 25 | 26 | @Override 27 | public void onReceive(Context context, Intent intent) { 28 | Bundle extras = getResultExtras(true); 29 | 30 | if (extras.containsKey(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE)) { 31 | languagePref = extras.getString(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE); 32 | } 33 | 34 | if (extras.containsKey(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES)) { 35 | supportedLanguagesList = extras.getStringArrayList(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES); 36 | 37 | JSArray languagesList = new JSArray(supportedLanguagesList); 38 | call.resolve(new JSObject().put("languages", languagesList)); 39 | return; 40 | } 41 | 42 | call.reject(ERROR); 43 | } 44 | 45 | public List getSupportedLanguages() { 46 | return supportedLanguagesList; 47 | } 48 | 49 | public String getLanguagePreference() { 50 | return languagePref; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /android/src/main/java/com/getcapacitor/community/speechrecognition/SpeechRecognition.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor.community.speechrecognition; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.speech.RecognitionListener; 9 | import android.speech.RecognizerIntent; 10 | import android.speech.SpeechRecognizer; 11 | import androidx.activity.result.ActivityResult; 12 | import com.getcapacitor.JSArray; 13 | import com.getcapacitor.JSObject; 14 | import com.getcapacitor.Logger; 15 | import com.getcapacitor.PermissionState; 16 | import com.getcapacitor.Plugin; 17 | import com.getcapacitor.PluginCall; 18 | import com.getcapacitor.PluginMethod; 19 | import com.getcapacitor.annotation.ActivityCallback; 20 | import com.getcapacitor.annotation.CapacitorPlugin; 21 | import com.getcapacitor.annotation.Permission; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Locale; 25 | import java.util.concurrent.locks.ReentrantLock; 26 | import org.json.JSONArray; 27 | 28 | @CapacitorPlugin( 29 | permissions = { @Permission(strings = { Manifest.permission.RECORD_AUDIO }, alias = SpeechRecognition.SPEECH_RECOGNITION) } 30 | ) 31 | public class SpeechRecognition extends Plugin implements Constants { 32 | 33 | public static final String TAG = "SpeechRecognition"; 34 | private static final String LISTENING_EVENT = "listeningState"; 35 | static final String SPEECH_RECOGNITION = "speechRecognition"; 36 | 37 | private Receiver languageReceiver; 38 | private SpeechRecognizer speechRecognizer; 39 | 40 | private final ReentrantLock lock = new ReentrantLock(); 41 | private boolean listening = false; 42 | 43 | private JSONArray previousPartialResults = new JSONArray(); 44 | 45 | @Override 46 | public void load() { 47 | super.load(); 48 | bridge 49 | .getWebView() 50 | .post(() -> { 51 | speechRecognizer = SpeechRecognizer.createSpeechRecognizer(bridge.getActivity()); 52 | SpeechRecognitionListener listener = new SpeechRecognitionListener(); 53 | speechRecognizer.setRecognitionListener(listener); 54 | Logger.info(getLogTag(), "Instantiated SpeechRecognizer in load()"); 55 | }); 56 | } 57 | 58 | @PluginMethod 59 | public void available(PluginCall call) { 60 | Logger.info(getLogTag(), "Called for available(): " + isSpeechRecognitionAvailable()); 61 | boolean val = isSpeechRecognitionAvailable(); 62 | JSObject result = new JSObject(); 63 | result.put("available", val); 64 | call.resolve(result); 65 | } 66 | 67 | @PluginMethod 68 | public void start(PluginCall call) { 69 | if (!isSpeechRecognitionAvailable()) { 70 | call.unavailable(NOT_AVAILABLE); 71 | return; 72 | } 73 | 74 | if (getPermissionState(SPEECH_RECOGNITION) != PermissionState.GRANTED) { 75 | call.reject(MISSING_PERMISSION); 76 | return; 77 | } 78 | 79 | String language = call.getString("language", Locale.getDefault().toString()); 80 | int maxResults = call.getInt("maxResults", MAX_RESULTS); 81 | String prompt = call.getString("prompt", null); 82 | boolean partialResults = call.getBoolean("partialResults", false); 83 | boolean popup = call.getBoolean("popup", false); 84 | beginListening(language, maxResults, prompt, partialResults, popup, call); 85 | } 86 | 87 | @PluginMethod 88 | public void stop(final PluginCall call) { 89 | try { 90 | stopListening(); 91 | } catch (Exception ex) { 92 | call.reject(ex.getLocalizedMessage()); 93 | } 94 | } 95 | 96 | @PluginMethod 97 | public void getSupportedLanguages(PluginCall call) { 98 | if (languageReceiver == null) { 99 | languageReceiver = new Receiver(call); 100 | } 101 | 102 | List supportedLanguages = languageReceiver.getSupportedLanguages(); 103 | if (supportedLanguages != null) { 104 | JSONArray languages = new JSONArray(supportedLanguages); 105 | call.resolve(new JSObject().put("languages", languages)); 106 | return; 107 | } 108 | 109 | Intent detailsIntent = new Intent(RecognizerIntent.ACTION_GET_LANGUAGE_DETAILS); 110 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 111 | detailsIntent.setPackage("com.google.android.googlequicksearchbox"); 112 | } 113 | bridge.getActivity().sendOrderedBroadcast(detailsIntent, null, languageReceiver, null, Activity.RESULT_OK, null, null); 114 | } 115 | 116 | @PluginMethod 117 | public void isListening(PluginCall call) { 118 | call.resolve(new JSObject().put("listening", SpeechRecognition.this.listening)); 119 | } 120 | 121 | @ActivityCallback 122 | private void listeningResult(PluginCall call, ActivityResult result) { 123 | if (call == null) { 124 | return; 125 | } 126 | 127 | int resultCode = result.getResultCode(); 128 | if (resultCode == Activity.RESULT_OK) { 129 | try { 130 | ArrayList matchesList = result.getData().getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS); 131 | JSObject resultObj = new JSObject(); 132 | resultObj.put("matches", new JSArray(matchesList)); 133 | call.resolve(resultObj); 134 | } catch (Exception ex) { 135 | call.reject(ex.getMessage()); 136 | } 137 | } else { 138 | call.reject(Integer.toString(resultCode)); 139 | } 140 | 141 | SpeechRecognition.this.lock.lock(); 142 | SpeechRecognition.this.listening(false); 143 | SpeechRecognition.this.lock.unlock(); 144 | } 145 | 146 | private boolean isSpeechRecognitionAvailable() { 147 | return SpeechRecognizer.isRecognitionAvailable(bridge.getContext()); 148 | } 149 | 150 | private void listening(boolean value) { 151 | this.listening = value; 152 | } 153 | 154 | private void beginListening( 155 | String language, 156 | int maxResults, 157 | String prompt, 158 | final boolean partialResults, 159 | boolean showPopup, 160 | PluginCall call 161 | ) { 162 | Logger.info(getLogTag(), "Beginning to listen for audible speech"); 163 | 164 | final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); 165 | intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); 166 | intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); 167 | intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults); 168 | intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, bridge.getActivity().getPackageName()); 169 | intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, partialResults); 170 | intent.putExtra("android.speech.extra.DICTATION_MODE", partialResults); 171 | 172 | if (prompt != null) { 173 | intent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); 174 | } 175 | 176 | if (showPopup) { 177 | startActivityForResult(call, intent, "listeningResult"); 178 | } else { 179 | bridge 180 | .getWebView() 181 | .post(() -> { 182 | try { 183 | SpeechRecognition.this.lock.lock(); 184 | 185 | if (speechRecognizer != null) { 186 | speechRecognizer.cancel(); 187 | speechRecognizer.destroy(); 188 | speechRecognizer = null; 189 | } 190 | 191 | speechRecognizer = SpeechRecognizer.createSpeechRecognizer(bridge.getActivity()); 192 | SpeechRecognitionListener listener = new SpeechRecognitionListener(); 193 | listener.setCall(call); 194 | listener.setPartialResults(partialResults); 195 | speechRecognizer.setRecognitionListener(listener); 196 | speechRecognizer.startListening(intent); 197 | SpeechRecognition.this.listening(true); 198 | if (partialResults) { 199 | call.resolve(); 200 | } 201 | } catch (Exception ex) { 202 | call.reject(ex.getMessage()); 203 | } finally { 204 | SpeechRecognition.this.lock.unlock(); 205 | } 206 | }); 207 | } 208 | } 209 | 210 | private void stopListening() { 211 | bridge 212 | .getWebView() 213 | .post(() -> { 214 | try { 215 | SpeechRecognition.this.lock.lock(); 216 | if (SpeechRecognition.this.listening) { 217 | speechRecognizer.stopListening(); 218 | SpeechRecognition.this.listening(false); 219 | } 220 | } catch (Exception ex) { 221 | throw ex; 222 | } finally { 223 | SpeechRecognition.this.lock.unlock(); 224 | } 225 | }); 226 | } 227 | 228 | private class SpeechRecognitionListener implements RecognitionListener { 229 | 230 | private PluginCall call; 231 | private boolean partialResults; 232 | 233 | public void setCall(PluginCall call) { 234 | this.call = call; 235 | } 236 | 237 | public void setPartialResults(boolean partialResults) { 238 | this.partialResults = partialResults; 239 | } 240 | 241 | @Override 242 | public void onReadyForSpeech(Bundle params) {} 243 | 244 | @Override 245 | public void onBeginningOfSpeech() { 246 | try { 247 | SpeechRecognition.this.lock.lock(); 248 | // Notify listeners that recording has started 249 | JSObject ret = new JSObject(); 250 | ret.put("status", "started"); 251 | SpeechRecognition.this.notifyListeners(LISTENING_EVENT, ret); 252 | } finally { 253 | SpeechRecognition.this.lock.unlock(); 254 | } 255 | } 256 | 257 | @Override 258 | public void onRmsChanged(float rmsdB) {} 259 | 260 | @Override 261 | public void onBufferReceived(byte[] buffer) {} 262 | 263 | @Override 264 | public void onEndOfSpeech() { 265 | bridge 266 | .getWebView() 267 | .post(() -> { 268 | try { 269 | SpeechRecognition.this.lock.lock(); 270 | SpeechRecognition.this.listening(false); 271 | 272 | JSObject ret = new JSObject(); 273 | ret.put("status", "stopped"); 274 | SpeechRecognition.this.notifyListeners(LISTENING_EVENT, ret); 275 | } finally { 276 | SpeechRecognition.this.lock.unlock(); 277 | } 278 | }); 279 | } 280 | 281 | @Override 282 | public void onError(int error) { 283 | SpeechRecognition.this.stopListening(); 284 | String errorMssg = getErrorText(error); 285 | 286 | if (this.call != null) { 287 | call.reject(errorMssg); 288 | } 289 | } 290 | 291 | @Override 292 | public void onResults(Bundle results) { 293 | ArrayList matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); 294 | 295 | try { 296 | JSArray jsArray = new JSArray(matches); 297 | 298 | if (this.call != null) { 299 | if (!this.partialResults) { 300 | this.call.resolve(new JSObject().put("status", "success").put("matches", jsArray)); 301 | } else { 302 | JSObject ret = new JSObject(); 303 | ret.put("matches", jsArray); 304 | notifyListeners("partialResults", ret); 305 | } 306 | } 307 | } catch (Exception ex) { 308 | this.call.resolve(new JSObject().put("status", "error").put("message", ex.getMessage())); 309 | } 310 | } 311 | 312 | @Override 313 | public void onPartialResults(Bundle partialResults) { 314 | ArrayList matches = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); 315 | JSArray matchesJSON = new JSArray(matches); 316 | 317 | try { 318 | if (matches != null && matches.size() > 0 && !previousPartialResults.equals(matchesJSON)) { 319 | previousPartialResults = matchesJSON; 320 | JSObject ret = new JSObject(); 321 | ret.put("matches", previousPartialResults); 322 | notifyListeners("partialResults", ret); 323 | } 324 | } catch (Exception ex) {} 325 | } 326 | 327 | @Override 328 | public void onEvent(int eventType, Bundle params) {} 329 | } 330 | 331 | private String getErrorText(int errorCode) { 332 | String message; 333 | switch (errorCode) { 334 | case SpeechRecognizer.ERROR_AUDIO: 335 | message = "Audio recording error"; 336 | break; 337 | case SpeechRecognizer.ERROR_CLIENT: 338 | message = "Client side error"; 339 | break; 340 | case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS: 341 | message = "Insufficient permissions"; 342 | break; 343 | case SpeechRecognizer.ERROR_NETWORK: 344 | message = "Network error"; 345 | break; 346 | case SpeechRecognizer.ERROR_NETWORK_TIMEOUT: 347 | message = "Network timeout"; 348 | break; 349 | case SpeechRecognizer.ERROR_NO_MATCH: 350 | message = "No match"; 351 | break; 352 | case SpeechRecognizer.ERROR_RECOGNIZER_BUSY: 353 | message = "RecognitionService busy"; 354 | break; 355 | case SpeechRecognizer.ERROR_SERVER: 356 | message = "error from server"; 357 | break; 358 | case SpeechRecognizer.ERROR_SPEECH_TIMEOUT: 359 | message = "No speech input"; 360 | break; 361 | default: 362 | message = "Didn't understand, please try again."; 363 | break; 364 | } 365 | return message; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /android/src/main/res/layout/bridge_layout_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Just a simple string 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/test/java/com/getcapacitor/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.getcapacitor; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | 14 | @Test 15 | public void addition_isCorrect() throws Exception { 16 | assertEquals(4, 2 + 2); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ios/Plugin.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; 11 | 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; 12 | 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; 13 | 50ADFF97201F53D600D50D53 /* PluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* PluginTests.swift */; }; 14 | 50ADFF99201F53D600D50D53 /* Plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* Plugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; 16 | 50ADFFA82020EE4F00D50D53 /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* Plugin.m */; }; 17 | 50E1A94820377CB70090CE1A /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* Plugin.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 50ADFF87201F53D600D50D53; 26 | remoteInfo = Plugin; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 50ADFF8B201F53D600D50D53 /* Plugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Plugin.h; sourceTree = ""; }; 34 | 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 50ADFF96201F53D600D50D53 /* PluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginTests.swift; sourceTree = ""; }; 37 | 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 50ADFFA72020EE4F00D50D53 /* Plugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Plugin.m; sourceTree = ""; }; 40 | 50E1A94720377CB70090CE1A /* Plugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; 41 | 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; 42 | 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; 43 | 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; 44 | F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; 45 | F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | 50ADFF84201F53D600D50D53 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, 54 | 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 50ADFF8E201F53D600D50D53 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, 63 | 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 50ADFF7E201F53D600D50D53 = { 71 | isa = PBXGroup; 72 | children = ( 73 | 50ADFF8A201F53D600D50D53 /* Plugin */, 74 | 50ADFF95201F53D600D50D53 /* PluginTests */, 75 | 50ADFF89201F53D600D50D53 /* Products */, 76 | 8C8E7744173064A9F6D438E3 /* Pods */, 77 | A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, 78 | ); 79 | sourceTree = ""; 80 | }; 81 | 50ADFF89201F53D600D50D53 /* Products */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 50ADFF88201F53D600D50D53 /* Plugin.framework */, 85 | 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 50ADFF8A201F53D600D50D53 /* Plugin */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 50E1A94720377CB70090CE1A /* Plugin.swift */, 94 | 50ADFF8B201F53D600D50D53 /* Plugin.h */, 95 | 50ADFFA72020EE4F00D50D53 /* Plugin.m */, 96 | 50ADFF8C201F53D600D50D53 /* Info.plist */, 97 | ); 98 | path = Plugin; 99 | sourceTree = ""; 100 | }; 101 | 50ADFF95201F53D600D50D53 /* PluginTests */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 50ADFF96201F53D600D50D53 /* PluginTests.swift */, 105 | 50ADFF98201F53D600D50D53 /* Info.plist */, 106 | ); 107 | path = PluginTests; 108 | sourceTree = ""; 109 | }; 110 | 8C8E7744173064A9F6D438E3 /* Pods */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, 114 | 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, 115 | 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, 116 | F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, 117 | ); 118 | name = Pods; 119 | sourceTree = ""; 120 | }; 121 | A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 50ADFFA52020D75100D50D53 /* Capacitor.framework */, 125 | 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, 126 | F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, 127 | ); 128 | name = Frameworks; 129 | sourceTree = ""; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXHeadersBuildPhase section */ 134 | 50ADFF85201F53D600D50D53 /* Headers */ = { 135 | isa = PBXHeadersBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | 50ADFF99201F53D600D50D53 /* Plugin.h in Headers */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXHeadersBuildPhase section */ 143 | 144 | /* Begin PBXNativeTarget section */ 145 | 50ADFF87201F53D600D50D53 /* Plugin */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; 148 | buildPhases = ( 149 | AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, 150 | 50ADFF83201F53D600D50D53 /* Sources */, 151 | 50ADFF84201F53D600D50D53 /* Frameworks */, 152 | 50ADFF85201F53D600D50D53 /* Headers */, 153 | 50ADFF86201F53D600D50D53 /* Resources */, 154 | ); 155 | buildRules = ( 156 | ); 157 | dependencies = ( 158 | ); 159 | name = Plugin; 160 | productName = Plugin; 161 | productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; 162 | productType = "com.apple.product-type.framework"; 163 | }; 164 | 50ADFF90201F53D600D50D53 /* PluginTests */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; 167 | buildPhases = ( 168 | 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, 169 | 50ADFF8D201F53D600D50D53 /* Sources */, 170 | 50ADFF8E201F53D600D50D53 /* Frameworks */, 171 | 50ADFF8F201F53D600D50D53 /* Resources */, 172 | CCA81D3B7E26D0D727D24C84 /* [CP] Embed Pods Frameworks */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, 178 | ); 179 | name = PluginTests; 180 | productName = PluginTests; 181 | productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; 182 | productType = "com.apple.product-type.bundle.unit-test"; 183 | }; 184 | /* End PBXNativeTarget section */ 185 | 186 | /* Begin PBXProject section */ 187 | 50ADFF7F201F53D600D50D53 /* Project object */ = { 188 | isa = PBXProject; 189 | attributes = { 190 | LastSwiftUpdateCheck = 0920; 191 | LastUpgradeCheck = 0920; 192 | ORGANIZATIONNAME = "Max Lynch"; 193 | TargetAttributes = { 194 | 50ADFF87201F53D600D50D53 = { 195 | CreatedOnToolsVersion = 9.2; 196 | LastSwiftMigration = 1100; 197 | ProvisioningStyle = Automatic; 198 | }; 199 | 50ADFF90201F53D600D50D53 = { 200 | CreatedOnToolsVersion = 9.2; 201 | LastSwiftMigration = 1100; 202 | ProvisioningStyle = Automatic; 203 | }; 204 | }; 205 | }; 206 | buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; 207 | compatibilityVersion = "Xcode 8.0"; 208 | developmentRegion = en; 209 | hasScannedForEncodings = 0; 210 | knownRegions = ( 211 | en, 212 | ); 213 | mainGroup = 50ADFF7E201F53D600D50D53; 214 | productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; 215 | projectDirPath = ""; 216 | projectRoot = ""; 217 | targets = ( 218 | 50ADFF87201F53D600D50D53 /* Plugin */, 219 | 50ADFF90201F53D600D50D53 /* PluginTests */, 220 | ); 221 | }; 222 | /* End PBXProject section */ 223 | 224 | /* Begin PBXResourcesBuildPhase section */ 225 | 50ADFF86201F53D600D50D53 /* Resources */ = { 226 | isa = PBXResourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | 50ADFF8F201F53D600D50D53 /* Resources */ = { 233 | isa = PBXResourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | }; 239 | /* End PBXResourcesBuildPhase section */ 240 | 241 | /* Begin PBXShellScriptBuildPhase section */ 242 | 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { 243 | isa = PBXShellScriptBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | inputPaths = ( 248 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 249 | "${PODS_ROOT}/Manifest.lock", 250 | ); 251 | name = "[CP] Check Pods Manifest.lock"; 252 | outputPaths = ( 253 | "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 258 | showEnvVarsInLog = 0; 259 | }; 260 | AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { 261 | isa = PBXShellScriptBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | inputPaths = ( 266 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 267 | "${PODS_ROOT}/Manifest.lock", 268 | ); 269 | name = "[CP] Check Pods Manifest.lock"; 270 | outputPaths = ( 271 | "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 276 | showEnvVarsInLog = 0; 277 | }; 278 | CCA81D3B7E26D0D727D24C84 /* [CP] Embed Pods Frameworks */ = { 279 | isa = PBXShellScriptBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | ); 283 | inputPaths = ( 284 | "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", 285 | "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", 286 | "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", 287 | ); 288 | name = "[CP] Embed Pods Frameworks"; 289 | outputPaths = ( 290 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", 291 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | shellPath = /bin/sh; 295 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; 296 | showEnvVarsInLog = 0; 297 | }; 298 | /* End PBXShellScriptBuildPhase section */ 299 | 300 | /* Begin PBXSourcesBuildPhase section */ 301 | 50ADFF83201F53D600D50D53 /* Sources */ = { 302 | isa = PBXSourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | 50E1A94820377CB70090CE1A /* Plugin.swift in Sources */, 306 | 50ADFFA82020EE4F00D50D53 /* Plugin.m in Sources */, 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | }; 310 | 50ADFF8D201F53D600D50D53 /* Sources */ = { 311 | isa = PBXSourcesBuildPhase; 312 | buildActionMask = 2147483647; 313 | files = ( 314 | 50ADFF97201F53D600D50D53 /* PluginTests.swift in Sources */, 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | /* End PBXSourcesBuildPhase section */ 319 | 320 | /* Begin PBXTargetDependency section */ 321 | 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { 322 | isa = PBXTargetDependency; 323 | target = 50ADFF87201F53D600D50D53 /* Plugin */; 324 | targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; 325 | }; 326 | /* End PBXTargetDependency section */ 327 | 328 | /* Begin XCBuildConfiguration section */ 329 | 50ADFF9A201F53D600D50D53 /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ALWAYS_SEARCH_USER_PATHS = NO; 333 | CLANG_ANALYZER_NONNULL = YES; 334 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 335 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 336 | CLANG_CXX_LIBRARY = "libc++"; 337 | CLANG_ENABLE_MODULES = YES; 338 | CLANG_ENABLE_OBJC_ARC = YES; 339 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 340 | CLANG_WARN_BOOL_CONVERSION = YES; 341 | CLANG_WARN_COMMA = YES; 342 | CLANG_WARN_CONSTANT_CONVERSION = YES; 343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 345 | CLANG_WARN_EMPTY_BODY = YES; 346 | CLANG_WARN_ENUM_CONVERSION = YES; 347 | CLANG_WARN_INFINITE_RECURSION = YES; 348 | CLANG_WARN_INT_CONVERSION = YES; 349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 350 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 351 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 352 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 353 | CLANG_WARN_STRICT_PROTOTYPES = YES; 354 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 355 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 356 | CLANG_WARN_UNREACHABLE_CODE = YES; 357 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 358 | CODE_SIGN_IDENTITY = "iPhone Developer"; 359 | COPY_PHASE_STRIP = NO; 360 | CURRENT_PROJECT_VERSION = 1; 361 | DEBUG_INFORMATION_FORMAT = dwarf; 362 | ENABLE_STRICT_OBJC_MSGSEND = YES; 363 | ENABLE_TESTABILITY = YES; 364 | GCC_C_LANGUAGE_STANDARD = gnu11; 365 | GCC_DYNAMIC_NO_PIC = NO; 366 | GCC_NO_COMMON_BLOCKS = YES; 367 | GCC_OPTIMIZATION_LEVEL = 0; 368 | GCC_PREPROCESSOR_DEFINITIONS = ( 369 | "DEBUG=1", 370 | "$(inherited)", 371 | ); 372 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 373 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 374 | GCC_WARN_UNDECLARED_SELECTOR = YES; 375 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 376 | GCC_WARN_UNUSED_FUNCTION = YES; 377 | GCC_WARN_UNUSED_VARIABLE = YES; 378 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 379 | MTL_ENABLE_DEBUG_INFO = YES; 380 | ONLY_ACTIVE_ARCH = YES; 381 | SDKROOT = iphoneos; 382 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 383 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 384 | VERSIONING_SYSTEM = "apple-generic"; 385 | VERSION_INFO_PREFIX = ""; 386 | }; 387 | name = Debug; 388 | }; 389 | 50ADFF9B201F53D600D50D53 /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ALWAYS_SEARCH_USER_PATHS = NO; 393 | CLANG_ANALYZER_NONNULL = YES; 394 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 396 | CLANG_CXX_LIBRARY = "libc++"; 397 | CLANG_ENABLE_MODULES = YES; 398 | CLANG_ENABLE_OBJC_ARC = YES; 399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 400 | CLANG_WARN_BOOL_CONVERSION = YES; 401 | CLANG_WARN_COMMA = YES; 402 | CLANG_WARN_CONSTANT_CONVERSION = YES; 403 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 404 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 405 | CLANG_WARN_EMPTY_BODY = YES; 406 | CLANG_WARN_ENUM_CONVERSION = YES; 407 | CLANG_WARN_INFINITE_RECURSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 416 | CLANG_WARN_UNREACHABLE_CODE = YES; 417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 418 | CODE_SIGN_IDENTITY = "iPhone Developer"; 419 | COPY_PHASE_STRIP = NO; 420 | CURRENT_PROJECT_VERSION = 1; 421 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 422 | ENABLE_NS_ASSERTIONS = NO; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | GCC_C_LANGUAGE_STANDARD = gnu11; 425 | GCC_NO_COMMON_BLOCKS = YES; 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 433 | MTL_ENABLE_DEBUG_INFO = NO; 434 | SDKROOT = iphoneos; 435 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 436 | VALIDATE_PRODUCT = YES; 437 | VERSIONING_SYSTEM = "apple-generic"; 438 | VERSION_INFO_PREFIX = ""; 439 | }; 440 | name = Release; 441 | }; 442 | 50ADFF9D201F53D600D50D53 /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; 445 | buildSettings = { 446 | CLANG_ENABLE_MODULES = YES; 447 | CODE_SIGN_IDENTITY = ""; 448 | CODE_SIGN_STYLE = Automatic; 449 | DEFINES_MODULE = YES; 450 | DYLIB_COMPATIBILITY_VERSION = 1; 451 | DYLIB_CURRENT_VERSION = 1; 452 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 453 | INFOPLIST_FILE = Plugin/Info.plist; 454 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 455 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; 457 | ONLY_ACTIVE_ARCH = YES; 458 | PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; 459 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 460 | SKIP_INSTALL = YES; 461 | SUPPORTS_MACCATALYST = NO; 462 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 463 | SWIFT_VERSION = 5.0; 464 | TARGETED_DEVICE_FAMILY = "1,2"; 465 | }; 466 | name = Debug; 467 | }; 468 | 50ADFF9E201F53D600D50D53 /* Release */ = { 469 | isa = XCBuildConfiguration; 470 | baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; 471 | buildSettings = { 472 | CLANG_ENABLE_MODULES = YES; 473 | CODE_SIGN_IDENTITY = ""; 474 | CODE_SIGN_STYLE = Automatic; 475 | DEFINES_MODULE = YES; 476 | DYLIB_COMPATIBILITY_VERSION = 1; 477 | DYLIB_CURRENT_VERSION = 1; 478 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 479 | INFOPLIST_FILE = Plugin/Info.plist; 480 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 481 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 482 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; 483 | ONLY_ACTIVE_ARCH = NO; 484 | PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; 485 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 486 | SKIP_INSTALL = YES; 487 | SUPPORTS_MACCATALYST = NO; 488 | SWIFT_VERSION = 5.0; 489 | TARGETED_DEVICE_FAMILY = "1,2"; 490 | }; 491 | name = Release; 492 | }; 493 | 50ADFFA0201F53D600D50D53 /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; 496 | buildSettings = { 497 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 498 | CODE_SIGN_STYLE = Automatic; 499 | INFOPLIST_FILE = PluginTests/Info.plist; 500 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 501 | PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | SWIFT_VERSION = 5.0; 504 | TARGETED_DEVICE_FAMILY = "1,2"; 505 | }; 506 | name = Debug; 507 | }; 508 | 50ADFFA1201F53D600D50D53 /* Release */ = { 509 | isa = XCBuildConfiguration; 510 | baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; 511 | buildSettings = { 512 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 513 | CODE_SIGN_STYLE = Automatic; 514 | INFOPLIST_FILE = PluginTests/Info.plist; 515 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 516 | PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | SWIFT_VERSION = 5.0; 519 | TARGETED_DEVICE_FAMILY = "1,2"; 520 | }; 521 | name = Release; 522 | }; 523 | /* End XCBuildConfiguration section */ 524 | 525 | /* Begin XCConfigurationList section */ 526 | 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { 527 | isa = XCConfigurationList; 528 | buildConfigurations = ( 529 | 50ADFF9A201F53D600D50D53 /* Debug */, 530 | 50ADFF9B201F53D600D50D53 /* Release */, 531 | ); 532 | defaultConfigurationIsVisible = 0; 533 | defaultConfigurationName = Release; 534 | }; 535 | 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { 536 | isa = XCConfigurationList; 537 | buildConfigurations = ( 538 | 50ADFF9D201F53D600D50D53 /* Debug */, 539 | 50ADFF9E201F53D600D50D53 /* Release */, 540 | ); 541 | defaultConfigurationIsVisible = 0; 542 | defaultConfigurationName = Release; 543 | }; 544 | 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { 545 | isa = XCConfigurationList; 546 | buildConfigurations = ( 547 | 50ADFFA0201F53D600D50D53 /* Debug */, 548 | 50ADFFA1201F53D600D50D53 /* Release */, 549 | ); 550 | defaultConfigurationIsVisible = 0; 551 | defaultConfigurationName = Release; 552 | }; 553 | /* End XCConfigurationList section */ 554 | }; 555 | rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; 556 | } 557 | -------------------------------------------------------------------------------- /ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Plugin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ios/Plugin/Plugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //! Project version number for Plugin. 4 | FOUNDATION_EXPORT double PluginVersionNumber; 5 | 6 | //! Project version string for Plugin. 7 | FOUNDATION_EXPORT const unsigned char PluginVersionString[]; 8 | 9 | // In this header, you should import all the public headers of your framework using statements like #import 10 | 11 | -------------------------------------------------------------------------------- /ios/Plugin/Plugin.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | // Define the plugin using the CAP_PLUGIN Macro, and 5 | // each method the plugin supports using the CAP_PLUGIN_METHOD macro. 6 | CAP_PLUGIN(SpeechRecognition, "SpeechRecognition", 7 | CAP_PLUGIN_METHOD(available, CAPPluginReturnPromise); 8 | CAP_PLUGIN_METHOD(start, CAPPluginReturnPromise); 9 | CAP_PLUGIN_METHOD(stop, CAPPluginReturnPromise); 10 | CAP_PLUGIN_METHOD(getSupportedLanguages, CAPPluginReturnPromise); 11 | CAP_PLUGIN_METHOD(hasPermission, CAPPluginReturnPromise); 12 | CAP_PLUGIN_METHOD(isListening, CAPPluginReturnPromise); 13 | CAP_PLUGIN_METHOD(requestPermission, CAPPluginReturnPromise); 14 | CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise); 15 | CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise); 16 | CAP_PLUGIN_METHOD(removeAllListeners, CAPPluginReturnPromise); 17 | ) 18 | -------------------------------------------------------------------------------- /ios/Plugin/Plugin.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Capacitor 3 | import Speech 4 | 5 | @objc(SpeechRecognition) 6 | public class SpeechRecognition: CAPPlugin { 7 | 8 | let defaultMatches = 5 9 | let messageMissingPermission = "Missing permission" 10 | let messageAccessDenied = "User denied access to speech recognition" 11 | let messageRestricted = "Speech recognition restricted on this device" 12 | let messageNotDetermined = "Speech recognition not determined on this device" 13 | let messageAccessDeniedMicrophone = "User denied access to microphone" 14 | let messageOngoing = "Ongoing speech recognition" 15 | let messageUnknown = "Unknown error occured" 16 | 17 | private var speechRecognizer: SFSpeechRecognizer? 18 | private var audioEngine: AVAudioEngine? 19 | private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest? 20 | private var recognitionTask: SFSpeechRecognitionTask? 21 | 22 | @objc func available(_ call: CAPPluginCall) { 23 | guard let recognizer = SFSpeechRecognizer() else { 24 | call.resolve([ 25 | "available": false 26 | ]) 27 | return 28 | } 29 | call.resolve([ 30 | "available": recognizer.isAvailable 31 | ]) 32 | } 33 | 34 | @objc func start(_ call: CAPPluginCall) { 35 | if self.audioEngine != nil { 36 | if self.audioEngine!.isRunning { 37 | call.reject(self.messageOngoing) 38 | return 39 | } 40 | } 41 | 42 | let status: SFSpeechRecognizerAuthorizationStatus = SFSpeechRecognizer.authorizationStatus() 43 | if status != SFSpeechRecognizerAuthorizationStatus.authorized { 44 | call.reject(self.messageMissingPermission) 45 | return 46 | } 47 | 48 | AVAudioSession.sharedInstance().requestRecordPermission { (granted) in 49 | if !granted { 50 | call.reject(self.messageAccessDeniedMicrophone) 51 | return 52 | } 53 | 54 | let language: String = call.getString("language") ?? "en-US" 55 | let maxResults: Int = call.getInt("maxResults") ?? self.defaultMatches 56 | let partialResults: Bool = call.getBool("partialResults") ?? false 57 | 58 | if self.recognitionTask != nil { 59 | self.recognitionTask?.cancel() 60 | self.recognitionTask = nil 61 | } 62 | 63 | self.audioEngine = AVAudioEngine.init() 64 | self.speechRecognizer = SFSpeechRecognizer.init(locale: Locale(identifier: language)) 65 | 66 | let audioSession: AVAudioSession = AVAudioSession.sharedInstance() 67 | do { 68 | try audioSession.setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker) 69 | try audioSession.setMode(AVAudioSession.Mode.default) 70 | do { 71 | try audioSession.setActive(true, options: AVAudioSession.SetActiveOptions.notifyOthersOnDeactivation) 72 | } catch { 73 | call.reject("Microphone is already in use by another application.") 74 | return 75 | } 76 | } catch { 77 | 78 | } 79 | 80 | self.recognitionRequest = SFSpeechAudioBufferRecognitionRequest() 81 | self.recognitionRequest?.shouldReportPartialResults = partialResults 82 | 83 | let inputNode: AVAudioInputNode = self.audioEngine!.inputNode 84 | let format: AVAudioFormat = inputNode.outputFormat(forBus: 0) 85 | 86 | self.recognitionTask = self.speechRecognizer?.recognitionTask(with: self.recognitionRequest!, resultHandler: { (result, error) in 87 | if result != nil { 88 | let resultArray: NSMutableArray = NSMutableArray() 89 | var counter: Int = 0 90 | 91 | for transcription: SFTranscription in result!.transcriptions { 92 | if maxResults > 0 && counter < maxResults { 93 | resultArray.add(transcription.formattedString) 94 | } 95 | counter+=1 96 | } 97 | 98 | if partialResults { 99 | self.notifyListeners("partialResults", data: ["matches": resultArray]) 100 | } else { 101 | call.resolve([ 102 | "matches": resultArray 103 | ]) 104 | } 105 | 106 | if result!.isFinal { 107 | self.audioEngine!.stop() 108 | self.audioEngine?.inputNode.removeTap(onBus: 0) 109 | self.notifyListeners("listeningState", data: ["status": "stopped"]) 110 | self.recognitionTask = nil 111 | self.recognitionRequest = nil 112 | } 113 | } 114 | 115 | if error != nil { 116 | self.audioEngine!.stop() 117 | self.audioEngine?.inputNode.removeTap(onBus: 0) 118 | self.recognitionRequest = nil 119 | self.recognitionTask = nil 120 | self.notifyListeners("listeningState", data: ["status": "stopped"]) 121 | call.reject(error!.localizedDescription) 122 | } 123 | }) 124 | 125 | inputNode.installTap(onBus: 0, bufferSize: 1024, format: format) { (buffer: AVAudioPCMBuffer, _: AVAudioTime) in 126 | self.recognitionRequest?.append(buffer) 127 | } 128 | 129 | self.audioEngine?.prepare() 130 | do { 131 | try self.audioEngine?.start() 132 | self.notifyListeners("listeningState", data: ["status": "started"]) 133 | if partialResults { 134 | call.resolve() 135 | } 136 | } catch { 137 | call.reject(self.messageUnknown) 138 | } 139 | } 140 | } 141 | 142 | @objc func stop(_ call: CAPPluginCall) { 143 | DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { 144 | if let engine = self.audioEngine, engine.isRunning { 145 | engine.stop() 146 | self.recognitionRequest?.endAudio() 147 | self.notifyListeners("listeningState", data: ["status": "stopped"]) 148 | } 149 | call.resolve() 150 | } 151 | } 152 | 153 | @objc func isListening(_ call: CAPPluginCall) { 154 | let isListening = self.audioEngine?.isRunning ?? false 155 | call.resolve([ 156 | "listening": isListening 157 | ]) 158 | } 159 | 160 | @objc func getSupportedLanguages(_ call: CAPPluginCall) { 161 | let supportedLanguages: Set! = SFSpeechRecognizer.supportedLocales() as Set 162 | let languagesArr: NSMutableArray = NSMutableArray() 163 | 164 | for lang: Locale in supportedLanguages { 165 | languagesArr.add(lang.identifier) 166 | } 167 | 168 | call.resolve([ 169 | "languages": languagesArr 170 | ]) 171 | } 172 | 173 | @objc override public func checkPermissions(_ call: CAPPluginCall) { 174 | let status: SFSpeechRecognizerAuthorizationStatus = SFSpeechRecognizer.authorizationStatus() 175 | let permission: String 176 | switch status { 177 | case .authorized: 178 | permission = "granted" 179 | case .denied, .restricted: 180 | permission = "denied" 181 | case .notDetermined: 182 | permission = "prompt" 183 | @unknown default: 184 | permission = "prompt" 185 | } 186 | call.resolve(["speechRecognition": permission]) 187 | } 188 | 189 | @objc override public func requestPermissions(_ call: CAPPluginCall) { 190 | SFSpeechRecognizer.requestAuthorization { (status: SFSpeechRecognizerAuthorizationStatus) in 191 | DispatchQueue.main.async { 192 | switch status { 193 | case .authorized: 194 | AVAudioSession.sharedInstance().requestRecordPermission { (granted: Bool) in 195 | if granted { 196 | call.resolve(["speechRecognition": "granted"]) 197 | } else { 198 | call.resolve(["speechRecognition": "denied"]) 199 | } 200 | } 201 | break 202 | case .denied, .restricted, .notDetermined: 203 | self.checkPermissions(call) 204 | break 205 | @unknown default: 206 | self.checkPermissions(call) 207 | } 208 | } 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /ios/PluginTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ios/PluginTests/PluginTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import Capacitor 3 | @testable import Plugin 4 | 5 | class PluginTests: XCTestCase { 6 | 7 | func testEcho() { 8 | // This is an example of a functional test case for a plugin. 9 | // Use XCTAssert and related functions to verify your tests produce the correct results. 10 | 11 | let value = "Hello, World!" 12 | let plugin = MyPlugin() 13 | 14 | let call = CAPPluginCall(callbackId: "test", options: [ 15 | "value": value 16 | ], resolve: { (result, _) in 17 | let resultValue = result!.data["value"] as? String 18 | XCTAssertEqual(value, resultValue) 19 | }, reject: { (_) in 20 | XCTFail("Error shouldn't have been called") 21 | }) 22 | 23 | plugin.echo(call!) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '14.0' 2 | 3 | def capacitor_pods 4 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 5 | use_frameworks! 6 | pod 'Capacitor', :path => '../node_modules/@capacitor/ios' 7 | pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios' 8 | end 9 | 10 | target 'Plugin' do 11 | capacitor_pods 12 | end 13 | 14 | target 'PluginTests' do 15 | capacitor_pods 16 | end -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@capacitor-community/speech-recognition", 3 | "version": "7.0.1", 4 | "description": "A native plugin for speech recognition", 5 | "main": "dist/plugin.cjs.js", 6 | "module": "dist/esm/index.js", 7 | "types": "dist/esm/index.d.ts", 8 | "unpkg": "dist/plugin.js", 9 | "files": [ 10 | "android/src/main/", 11 | "android/build.gradle", 12 | "dist/", 13 | "ios/Plugin/", 14 | "CapacitorCommunitySpeechRecognition.podspec" 15 | ], 16 | "author": "Priyank Patel ", 17 | "contributors": [ 18 | "Matteo Padovano " 19 | ], 20 | "license": "MIT", 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/capacitor-community/speech-recognition.git" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/capacitor-community/speech-recognition/issues" 27 | }, 28 | "keywords": [ 29 | "capacitor", 30 | "plugin", 31 | "native" 32 | ], 33 | "scripts": { 34 | "verify": "npm run verify:ios && npm run verify:android && npm run verify:web", 35 | "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..", 36 | "verify:android": "cd android && ./gradlew clean build test && cd ..", 37 | "verify:web": "npm run build", 38 | "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint", 39 | "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format", 40 | "eslint": "eslint . --ext ts", 41 | "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java", 42 | "swiftlint": "node-swiftlint", 43 | "docgen": "docgen --api SpeechRecognitionPlugin --output-readme README.md --output-json dist/docs.json", 44 | "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs", 45 | "clean": "rimraf ./dist", 46 | "watch": "tsc --watch", 47 | "prepublishOnly": "npm run build", 48 | "release": "standard-version" 49 | }, 50 | "devDependencies": { 51 | "@capacitor/android": "^7.0.0", 52 | "@capacitor/cli": "^7.0.0", 53 | "@capacitor/core": "^7.0.0", 54 | "@capacitor/docgen": "^0.3.0", 55 | "@capacitor/ios": "^7.0.0", 56 | "@ionic/eslint-config": "^0.4.0", 57 | "@ionic/prettier-config": "^4.0.0", 58 | "@ionic/swiftlint-config": "^2.0.0", 59 | "eslint": "^8.57.0", 60 | "prettier": "^3.4.2", 61 | "prettier-plugin-java": "^2.6.6", 62 | "rimraf": "^6.0.1", 63 | "rollup": "^4.30.1", 64 | "standard-version": "9.5.0", 65 | "swiftlint": "^2.0.0", 66 | "typescript": "4.1.5" 67 | }, 68 | "peerDependencies": { 69 | "@capacitor/core": ">=7.0.0" 70 | }, 71 | "prettier": "@ionic/prettier-config", 72 | "swiftlint": "@ionic/swiftlint-config", 73 | "eslintConfig": { 74 | "extends": "@ionic/eslint-config/recommended" 75 | }, 76 | "capacitor": { 77 | "ios": { 78 | "src": "ios" 79 | }, 80 | "android": { 81 | "src": "android" 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | input: 'dist/esm/index.js', 3 | output: [ 4 | { 5 | file: 'dist/plugin.js', 6 | format: 'iife', 7 | name: 'capacitorSpeechRecognition', 8 | globals: { 9 | '@capacitor/core': 'capacitorExports', 10 | }, 11 | sourcemap: true, 12 | inlineDynamicImports: true, 13 | }, 14 | { 15 | file: 'dist/plugin.cjs.js', 16 | format: 'cjs', 17 | sourcemap: true, 18 | inlineDynamicImports: true, 19 | }, 20 | ], 21 | external: ['@capacitor/core'], 22 | }; 23 | -------------------------------------------------------------------------------- /src/definitions.ts: -------------------------------------------------------------------------------- 1 | import type { PermissionState, PluginListenerHandle } from '@capacitor/core'; 2 | 3 | export interface PermissionStatus { 4 | /** 5 | * Permission state for speechRecognition alias. 6 | * 7 | * On Android it requests/checks RECORD_AUDIO permission 8 | * 9 | * On iOS it requests/checks the speech recognition and microphone permissions. 10 | * 11 | * @since 5.0.0 12 | */ 13 | speechRecognition: PermissionState; 14 | } 15 | 16 | export interface SpeechRecognitionPlugin { 17 | /** 18 | * This method will check if speech recognition feature is available on the device. 19 | * @param none 20 | * @returns available - boolean true/false for availability 21 | */ 22 | available(): Promise<{ available: boolean }>; 23 | /** 24 | * This method will start to listen for utterance. 25 | * 26 | * if `partialResults` is `true`, the function respond directly without result and 27 | * event `partialResults` will be emit for each partial result, until stopped. 28 | * 29 | * @param options 30 | * @returns void or array of string results 31 | */ 32 | start(options?: UtteranceOptions): Promise<{ matches?: string[] }>; 33 | /** 34 | * This method will stop listening for utterance 35 | * @param none 36 | * @returns void 37 | */ 38 | stop(): Promise; 39 | /** 40 | * This method will return list of languages supported by the speech recognizer. 41 | * 42 | * It's not available on Android 13 and newer. 43 | * 44 | * @param none 45 | * @returns languages - array string of languages 46 | */ 47 | getSupportedLanguages(): Promise<{ languages: any[] }>; 48 | /** 49 | * This method will check if speech recognition is listening. 50 | * @param none 51 | * @returns boolean true/false if speech recognition is currently listening 52 | * 53 | * @since 5.1.0 54 | */ 55 | isListening(): Promise<{ listening: boolean }>; 56 | /** 57 | * Check the speech recognition permission. 58 | * 59 | * @since 5.0.0 60 | */ 61 | checkPermissions(): Promise; 62 | /** 63 | * Request the speech recognition permission. 64 | * 65 | * @since 5.0.0 66 | */ 67 | requestPermissions(): Promise; 68 | /** 69 | * Called when partialResults set to true and result received. 70 | * 71 | * On Android it doesn't work if popup is true. 72 | * 73 | * Provides partial result. 74 | * 75 | * @since 2.0.2 76 | */ 77 | addListener( 78 | eventName: 'partialResults', 79 | listenerFunc: (data: { matches: string[] }) => void, 80 | ): Promise; 81 | 82 | /** 83 | * Called when listening state changed. 84 | * 85 | * @since 5.1.0 86 | */ 87 | addListener( 88 | eventName: 'listeningState', 89 | listenerFunc: (data: { status: 'started' | 'stopped' }) => void, 90 | ): Promise; 91 | /** 92 | * Remove all the listeners that are attached to this plugin. 93 | * 94 | * @since 4.0.0 95 | */ 96 | removeAllListeners(): Promise; 97 | } 98 | 99 | export interface UtteranceOptions { 100 | /** 101 | * key returned from `getSupportedLanguages()` 102 | */ 103 | language?: string; 104 | /** 105 | * maximum number of results to return (5 is max) 106 | */ 107 | maxResults?: number; 108 | /** 109 | * prompt message to display on popup (Android only) 110 | */ 111 | prompt?: string; 112 | /** 113 | * display popup window when listening for utterance (Android only) 114 | */ 115 | popup?: boolean; 116 | /** 117 | * return partial results if found 118 | */ 119 | partialResults?: boolean; 120 | } 121 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { registerPlugin } from '@capacitor/core'; 2 | 3 | import type { SpeechRecognitionPlugin } from './definitions'; 4 | 5 | const SpeechRecognition = registerPlugin('SpeechRecognition', { 6 | web: () => import('./web').then((m) => new m.SpeechRecognitionWeb()), 7 | }); 8 | 9 | export * from './definitions'; 10 | export { SpeechRecognition }; 11 | -------------------------------------------------------------------------------- /src/web.ts: -------------------------------------------------------------------------------- 1 | import { WebPlugin } from '@capacitor/core'; 2 | 3 | import type { PermissionStatus, SpeechRecognitionPlugin, UtteranceOptions } from './definitions'; 4 | 5 | export class SpeechRecognitionWeb extends WebPlugin implements SpeechRecognitionPlugin { 6 | available(): Promise<{ available: boolean }> { 7 | throw this.unimplemented('Method not implemented on web.'); 8 | } 9 | start(_options?: UtteranceOptions): Promise<{ matches?: string[] }> { 10 | throw this.unimplemented('Method not implemented on web.'); 11 | } 12 | stop(): Promise { 13 | throw this.unimplemented('Method not implemented on web.'); 14 | } 15 | getSupportedLanguages(): Promise<{ languages: any[] }> { 16 | throw this.unimplemented('Method not implemented on web.'); 17 | } 18 | hasPermission(): Promise<{ permission: boolean }> { 19 | throw this.unimplemented('Method not implemented on web.'); 20 | } 21 | isListening(): Promise<{ listening: boolean }> { 22 | throw this.unimplemented('Method not implemented on web.'); 23 | } 24 | requestPermission(): Promise { 25 | throw this.unimplemented('Method not implemented on web.'); 26 | } 27 | checkPermissions(): Promise { 28 | throw this.unimplemented('Method not implemented on web.'); 29 | } 30 | requestPermissions(): Promise { 31 | throw this.unimplemented('Method not implemented on web.'); 32 | } 33 | } 34 | 35 | const SpeechRecognition = new SpeechRecognitionWeb(); 36 | 37 | export { SpeechRecognition }; 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowUnreachableCode": false, 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "inlineSources": true, 7 | "lib": ["dom", "es2017"], 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noFallthroughCasesInSwitch": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "outDir": "dist/esm", 14 | "pretty": true, 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "es2017" 18 | }, 19 | "files": ["src/index.ts"] 20 | } 21 | --------------------------------------------------------------------------------