@capgo/capacitor-audio-session 7.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
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 = 'CapgoCapacitorPluginAudioSession'
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/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.exclude_files = '**/node_modules/**/*', '**/examples/**/*'
15
+ s.ios.deployment_target = '14.0'
16
+ s.dependency 'Capacitor'
17
+ s.swift_version = '5.1'
18
+ end
package/Package.swift ADDED
@@ -0,0 +1,28 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapgoCapacitorPluginAudioSession",
6
+ platforms: [.iOS(.v14)],
7
+ products: [
8
+ .library(
9
+ name: "CapgoCapacitorPluginAudioSession",
10
+ targets: ["AudioSessionPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.4.2")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "AudioSessionPlugin",
18
+ dependencies: [
19
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
+ .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ ],
22
+ path: "ios/Sources/AudioSessionPlugin"),
23
+ .testTarget(
24
+ name: "AudioSessionPluginTests",
25
+ dependencies: ["AudioSessionPlugin"],
26
+ path: "ios/Tests/AudioSessionPluginTests")
27
+ ]
28
+ )
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # @capgo/capacitor-plugin-audiosession
2
+ <a href="https://capgo.app/"><img src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/></a>
3
+
4
+ <div align="center">
5
+ <h2><a href="https://capgo.app/?ref=plugin"> ➡️ Get Instant updates for your App with Capgo</a></h2>
6
+ <h2><a href="https://capgo.app/consulting/?ref=plugin"> Missing a feature? We’ll build the plugin for you 💪</a></h2>
7
+ </div>
8
+
9
+
10
+ **This plugin works on iOS only.**
11
+
12
+ This plugin is a port of <https://github.com/saghul/cordova-plugin-audioroute> and allows iOS applications to get notified about audio session interruptions and route changes (for example when a headset is connected). To query and override the audio device in use is also supported.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @capgo/capacitor-plugin-audiosession
18
+ npx cap sync
19
+ ```
20
+
21
+ For now this plugin works only in Capacitor 4.0+.
22
+
23
+ ## API
24
+
25
+ <docgen-index>
26
+
27
+ * [`currentOutputs()`](#currentoutputs)
28
+ * [`overrideOutput(...)`](#overrideoutput)
29
+ * [`addListener('routeChanged', ...)`](#addlistenerroutechanged-)
30
+ * [`addListener('interruption', ...)`](#addlistenerinterruption-)
31
+ * [Interfaces](#interfaces)
32
+ * [Type Aliases](#type-aliases)
33
+ * [Enums](#enums)
34
+
35
+ </docgen-index>
36
+
37
+ <docgen-api>
38
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
39
+
40
+ iOS-only plugin to query and control the audio session output and listen to
41
+ route changes and interruptions.
42
+
43
+ ### currentOutputs()
44
+
45
+ ```typescript
46
+ currentOutputs() => Promise<AudioSessionPorts[]>
47
+ ```
48
+
49
+ Get the current active audio output routes.
50
+
51
+ On web and non-iOS platforms, this resolves to an empty array.
52
+
53
+ **Returns:** <code>Promise&lt;AudioSessionPorts[]&gt;</code>
54
+
55
+ --------------------
56
+
57
+
58
+ ### overrideOutput(...)
59
+
60
+ ```typescript
61
+ overrideOutput(type: OutputOverrideType) => Promise<OverrideResult>
62
+ ```
63
+
64
+ Override the current audio output route.
65
+
66
+ Use `speaker` to force playback through the built-in speaker, or
67
+ `default` to restore the system-selected route.
68
+
69
+ | Param | Type | Description |
70
+ | ---------- | ----------------------------------------------------------------- | --------------------------------- |
71
+ | **`type`** | <code><a href="#outputoverridetype">OutputOverrideType</a></code> | The desired output override type. |
72
+
73
+ **Returns:** <code>Promise&lt;<a href="#overrideresult">OverrideResult</a>&gt;</code>
74
+
75
+ --------------------
76
+
77
+
78
+ ### addListener('routeChanged', ...)
79
+
80
+ ```typescript
81
+ addListener(eventName: 'routeChanged', listenerFunc: RouteChangeListener) => Promise<PluginListenerHandle>
82
+ ```
83
+
84
+ Listen for audio route changes (e.g. headset connected/disconnected).
85
+
86
+ | Param | Type | Description |
87
+ | ------------------ | ------------------------------------------------------------------- | ---------------------------------------------- |
88
+ | **`eventName`** | <code>'routeChanged'</code> | The route change event name. |
89
+ | **`listenerFunc`** | <code><a href="#routechangelistener">RouteChangeListener</a></code> | Callback invoked with the route change reason. |
90
+
91
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
92
+
93
+ --------------------
94
+
95
+
96
+ ### addListener('interruption', ...)
97
+
98
+ ```typescript
99
+ addListener(eventName: 'interruption', listenerFunc: InterruptionListener) => Promise<PluginListenerHandle>
100
+ ```
101
+
102
+ Listen for audio session interruptions (e.g. incoming call) and their end.
103
+
104
+ | Param | Type | Description |
105
+ | ------------------ | --------------------------------------------------------------------- | -------------------------------------------- |
106
+ | **`eventName`** | <code>'interruption'</code> | The interruption event name. |
107
+ | **`listenerFunc`** | <code><a href="#interruptionlistener">InterruptionListener</a></code> | Callback invoked with the interruption type. |
108
+
109
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
110
+
111
+ --------------------
112
+
113
+
114
+ ### Interfaces
115
+
116
+
117
+ #### PluginListenerHandle
118
+
119
+ | Prop | Type |
120
+ | ------------ | ----------------------------------------- |
121
+ | **`remove`** | <code>() =&gt; Promise&lt;void&gt;</code> |
122
+
123
+
124
+ ### Type Aliases
125
+
126
+
127
+ #### OverrideResult
128
+
129
+ Result of an output override request.
130
+
131
+ <code>{ success: boolean; message: string; }</code>
132
+
133
+
134
+ #### OutputOverrideType
135
+
136
+ Output override type.
137
+ - `default`: Use the system-selected route.
138
+ - `speaker`: Force playback through the built-in speaker.
139
+
140
+ <code>'default' | 'speaker'</code>
141
+
142
+
143
+ #### RouteChangeListener
144
+
145
+ Listener called when the audio route changes.
146
+
147
+ <code>(reason: <a href="#routechangereasons">RouteChangeReasons</a>): void</code>
148
+
149
+
150
+ #### InterruptionListener
151
+
152
+ Listener called when the audio session is interrupted or ends.
153
+
154
+ <code>(type: <a href="#interruptiontypes">InterruptionTypes</a>): void</code>
155
+
156
+
157
+ ### Enums
158
+
159
+
160
+ #### AudioSessionPorts
161
+
162
+ | Members | Value |
163
+ | ----------------------- | ------------------------------- |
164
+ | **`AIR_PLAY`** | <code>'airplay'</code> |
165
+ | **`BLUETOOTH_LE`** | <code>'bluetooth-le'</code> |
166
+ | **`BLUETOOTH_HFP`** | <code>'bluetooth-hfp'</code> |
167
+ | **`BLUETOOTH_A2DP`** | <code>'bluetooth-a2dp'</code> |
168
+ | **`BUILT_IN_SPEAKER`** | <code>'builtin-speaker'</code> |
169
+ | **`BUILT_IN_RECEIVER`** | <code>'builtin-receiver'</code> |
170
+ | **`HDMI`** | <code>'hdmi'</code> |
171
+ | **`HEADPHONES`** | <code>'headphones'</code> |
172
+ | **`LINE_OUT`** | <code>'line-out'</code> |
173
+
174
+
175
+ #### RouteChangeReasons
176
+
177
+ | Members | Value |
178
+ | ------------------------------------ | --------------------------------------------- |
179
+ | **`NEW_DEVICE_AVAILABLE`** | <code>'new-device-available'</code> |
180
+ | **`OLD_DEVICE_UNAVAILABLE`** | <code>'old-device-unavailable'</code> |
181
+ | **`CATEGORY_CHANGE`** | <code>'category-change'</code> |
182
+ | **`OVERRIDE`** | <code>'override'</code> |
183
+ | **`WAKE_FROM_SLEEP`** | <code>'wake-from-sleep'</code> |
184
+ | **`NO_SUITABLE_ROUTE_FOR_CATEGORY`** | <code>'no-suitable-route-for-category'</code> |
185
+ | **`ROUTE_CONFIGURATION_CHANGE`** | <code>'route-config-change'</code> |
186
+ | **`UNKNOWN`** | <code>'unknown'</code> |
187
+
188
+
189
+ #### InterruptionTypes
190
+
191
+ | Members | Value |
192
+ | ----------- | -------------------- |
193
+ | **`BEGAN`** | <code>'began'</code> |
194
+ | **`ENDED`** | <code>'ended'</code> |
195
+
196
+ </docgen-api>
197
+
198
+ ## License
199
+
200
+ MIT
201
+
@@ -0,0 +1,58 @@
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 "ee.forgr.capacitor.audio.session"
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
+
51
+ dependencies {
52
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
53
+ implementation project(':capacitor-android')
54
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
55
+ testImplementation "junit:junit:$junitVersion"
56
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
57
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
58
+ }
@@ -0,0 +1,17 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ xmlns:tools="http://schemas.android.com/tools">
3
+ <uses-permission android:name="android.permission.USE_CREDENTIALS" />
4
+ <uses-permission android:name="android.permission.INTERNET"/>
5
+
6
+ <!-- Remove AD_ID permission if you're not using Facebook login to avoid Google Play Console errors -->
7
+ <!-- Uncomment the line below if you're only using Google/Apple login and getting AD_ID permission errors -->
8
+ <!-- <uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" /> -->
9
+
10
+ <application>
11
+ <provider
12
+ android:name="com.facebook.internal.FacebookInitProvider"
13
+ android:authorities="${applicationId}.FacebookInitProvider"
14
+ android:exported="false"
15
+ tools:node="remove" />
16
+ </application>
17
+ </manifest>
@@ -0,0 +1,32 @@
1
+ package ee.forgr.capacitor.audio.session;
2
+
3
+ import android.content.Intent;
4
+ import android.os.Build;
5
+ import android.util.Log;
6
+ import com.getcapacitor.JSArray;
7
+ import com.getcapacitor.JSObject;
8
+ import com.getcapacitor.Plugin;
9
+ import com.getcapacitor.PluginCall;
10
+ import com.getcapacitor.PluginMethod;
11
+ import com.getcapacitor.annotation.CapacitorPlugin;
12
+ import java.util.HashMap;
13
+ import org.json.JSONArray;
14
+ import org.json.JSONException;
15
+ import org.json.JSONObject;
16
+
17
+ @CapacitorPlugin(name = "AudioSession")
18
+ public class AudioSessionPlugin extends Plugin {
19
+
20
+ public static String LOG_TAG = "CapgoAudioSession";
21
+
22
+ @PluginMethod
23
+ public void currentOutputs(PluginCall call) {
24
+ call.resolve();
25
+ }
26
+
27
+ @PluginMethod
28
+ public void overrideOutput(PluginCall call) {
29
+ call.resolve();
30
+ }
31
+
32
+ }
File without changes
package/dist/docs.json ADDED
@@ -0,0 +1,320 @@
1
+ {
2
+ "api": {
3
+ "name": "AudioSessionPlugin",
4
+ "slug": "audiosessionplugin",
5
+ "docs": "iOS-only plugin to query and control the audio session output and listen to\nroute changes and interruptions.",
6
+ "tags": [],
7
+ "methods": [
8
+ {
9
+ "name": "currentOutputs",
10
+ "signature": "() => Promise<AudioSessionPorts[]>",
11
+ "parameters": [],
12
+ "returns": "Promise<AudioSessionPorts[]>",
13
+ "tags": [],
14
+ "docs": "Get the current active audio output routes.\n\nOn web and non-iOS platforms, this resolves to an empty array.",
15
+ "complexTypes": [
16
+ "AudioSessionPorts"
17
+ ],
18
+ "slug": "currentoutputs"
19
+ },
20
+ {
21
+ "name": "overrideOutput",
22
+ "signature": "(type: OutputOverrideType) => Promise<OverrideResult>",
23
+ "parameters": [
24
+ {
25
+ "name": "type",
26
+ "docs": "The desired output override type.",
27
+ "type": "OutputOverrideType"
28
+ }
29
+ ],
30
+ "returns": "Promise<OverrideResult>",
31
+ "tags": [
32
+ {
33
+ "name": "param",
34
+ "text": "type The desired output override type."
35
+ }
36
+ ],
37
+ "docs": "Override the current audio output route.\n\nUse `speaker` to force playback through the built-in speaker, or\n`default` to restore the system-selected route.",
38
+ "complexTypes": [
39
+ "OverrideResult",
40
+ "OutputOverrideType"
41
+ ],
42
+ "slug": "overrideoutput"
43
+ },
44
+ {
45
+ "name": "addListener",
46
+ "signature": "(eventName: 'routeChanged', listenerFunc: RouteChangeListener) => Promise<PluginListenerHandle>",
47
+ "parameters": [
48
+ {
49
+ "name": "eventName",
50
+ "docs": "The route change event name.",
51
+ "type": "'routeChanged'"
52
+ },
53
+ {
54
+ "name": "listenerFunc",
55
+ "docs": "Callback invoked with the route change reason.",
56
+ "type": "RouteChangeListener"
57
+ }
58
+ ],
59
+ "returns": "Promise<PluginListenerHandle>",
60
+ "tags": [
61
+ {
62
+ "name": "param",
63
+ "text": "eventName The route change event name."
64
+ },
65
+ {
66
+ "name": "param",
67
+ "text": "listenerFunc Callback invoked with the route change reason."
68
+ }
69
+ ],
70
+ "docs": "Listen for audio route changes (e.g. headset connected/disconnected).",
71
+ "complexTypes": [
72
+ "PluginListenerHandle",
73
+ "RouteChangeListener"
74
+ ],
75
+ "slug": "addlistenerroutechanged-"
76
+ },
77
+ {
78
+ "name": "addListener",
79
+ "signature": "(eventName: 'interruption', listenerFunc: InterruptionListener) => Promise<PluginListenerHandle>",
80
+ "parameters": [
81
+ {
82
+ "name": "eventName",
83
+ "docs": "The interruption event name.",
84
+ "type": "'interruption'"
85
+ },
86
+ {
87
+ "name": "listenerFunc",
88
+ "docs": "Callback invoked with the interruption type.",
89
+ "type": "InterruptionListener"
90
+ }
91
+ ],
92
+ "returns": "Promise<PluginListenerHandle>",
93
+ "tags": [
94
+ {
95
+ "name": "param",
96
+ "text": "eventName The interruption event name."
97
+ },
98
+ {
99
+ "name": "param",
100
+ "text": "listenerFunc Callback invoked with the interruption type."
101
+ }
102
+ ],
103
+ "docs": "Listen for audio session interruptions (e.g. incoming call) and their end.",
104
+ "complexTypes": [
105
+ "PluginListenerHandle",
106
+ "InterruptionListener"
107
+ ],
108
+ "slug": "addlistenerinterruption-"
109
+ }
110
+ ],
111
+ "properties": []
112
+ },
113
+ "interfaces": [
114
+ {
115
+ "name": "PluginListenerHandle",
116
+ "slug": "pluginlistenerhandle",
117
+ "docs": "",
118
+ "tags": [],
119
+ "methods": [],
120
+ "properties": [
121
+ {
122
+ "name": "remove",
123
+ "tags": [],
124
+ "docs": "",
125
+ "complexTypes": [],
126
+ "type": "() => Promise<void>"
127
+ }
128
+ ]
129
+ }
130
+ ],
131
+ "enums": [
132
+ {
133
+ "name": "AudioSessionPorts",
134
+ "slug": "audiosessionports",
135
+ "members": [
136
+ {
137
+ "name": "AIR_PLAY",
138
+ "value": "'airplay'",
139
+ "tags": [],
140
+ "docs": ""
141
+ },
142
+ {
143
+ "name": "BLUETOOTH_LE",
144
+ "value": "'bluetooth-le'",
145
+ "tags": [],
146
+ "docs": ""
147
+ },
148
+ {
149
+ "name": "BLUETOOTH_HFP",
150
+ "value": "'bluetooth-hfp'",
151
+ "tags": [],
152
+ "docs": ""
153
+ },
154
+ {
155
+ "name": "BLUETOOTH_A2DP",
156
+ "value": "'bluetooth-a2dp'",
157
+ "tags": [],
158
+ "docs": ""
159
+ },
160
+ {
161
+ "name": "BUILT_IN_SPEAKER",
162
+ "value": "'builtin-speaker'",
163
+ "tags": [],
164
+ "docs": ""
165
+ },
166
+ {
167
+ "name": "BUILT_IN_RECEIVER",
168
+ "value": "'builtin-receiver'",
169
+ "tags": [],
170
+ "docs": ""
171
+ },
172
+ {
173
+ "name": "HDMI",
174
+ "value": "'hdmi'",
175
+ "tags": [],
176
+ "docs": ""
177
+ },
178
+ {
179
+ "name": "HEADPHONES",
180
+ "value": "'headphones'",
181
+ "tags": [],
182
+ "docs": ""
183
+ },
184
+ {
185
+ "name": "LINE_OUT",
186
+ "value": "'line-out'",
187
+ "tags": [],
188
+ "docs": ""
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ "name": "RouteChangeReasons",
194
+ "slug": "routechangereasons",
195
+ "members": [
196
+ {
197
+ "name": "NEW_DEVICE_AVAILABLE",
198
+ "value": "'new-device-available'",
199
+ "tags": [],
200
+ "docs": ""
201
+ },
202
+ {
203
+ "name": "OLD_DEVICE_UNAVAILABLE",
204
+ "value": "'old-device-unavailable'",
205
+ "tags": [],
206
+ "docs": ""
207
+ },
208
+ {
209
+ "name": "CATEGORY_CHANGE",
210
+ "value": "'category-change'",
211
+ "tags": [],
212
+ "docs": ""
213
+ },
214
+ {
215
+ "name": "OVERRIDE",
216
+ "value": "'override'",
217
+ "tags": [],
218
+ "docs": ""
219
+ },
220
+ {
221
+ "name": "WAKE_FROM_SLEEP",
222
+ "value": "'wake-from-sleep'",
223
+ "tags": [],
224
+ "docs": ""
225
+ },
226
+ {
227
+ "name": "NO_SUITABLE_ROUTE_FOR_CATEGORY",
228
+ "value": "'no-suitable-route-for-category'",
229
+ "tags": [],
230
+ "docs": ""
231
+ },
232
+ {
233
+ "name": "ROUTE_CONFIGURATION_CHANGE",
234
+ "value": "'route-config-change'",
235
+ "tags": [],
236
+ "docs": ""
237
+ },
238
+ {
239
+ "name": "UNKNOWN",
240
+ "value": "'unknown'",
241
+ "tags": [],
242
+ "docs": ""
243
+ }
244
+ ]
245
+ },
246
+ {
247
+ "name": "InterruptionTypes",
248
+ "slug": "interruptiontypes",
249
+ "members": [
250
+ {
251
+ "name": "BEGAN",
252
+ "value": "'began'",
253
+ "tags": [],
254
+ "docs": ""
255
+ },
256
+ {
257
+ "name": "ENDED",
258
+ "value": "'ended'",
259
+ "tags": [],
260
+ "docs": ""
261
+ }
262
+ ]
263
+ }
264
+ ],
265
+ "typeAliases": [
266
+ {
267
+ "name": "OverrideResult",
268
+ "slug": "overrideresult",
269
+ "docs": "Result of an output override request.",
270
+ "types": [
271
+ {
272
+ "text": "{\n success: boolean;\n message: string;\n}",
273
+ "complexTypes": []
274
+ }
275
+ ]
276
+ },
277
+ {
278
+ "name": "OutputOverrideType",
279
+ "slug": "outputoverridetype",
280
+ "docs": "Output override type.\n- `default`: Use the system-selected route.\n- `speaker`: Force playback through the built-in speaker.",
281
+ "types": [
282
+ {
283
+ "text": "'default'",
284
+ "complexTypes": []
285
+ },
286
+ {
287
+ "text": "'speaker'",
288
+ "complexTypes": []
289
+ }
290
+ ]
291
+ },
292
+ {
293
+ "name": "RouteChangeListener",
294
+ "slug": "routechangelistener",
295
+ "docs": "Listener called when the audio route changes.",
296
+ "types": [
297
+ {
298
+ "text": "(reason: RouteChangeReasons): void",
299
+ "complexTypes": [
300
+ "RouteChangeReasons"
301
+ ]
302
+ }
303
+ ]
304
+ },
305
+ {
306
+ "name": "InterruptionListener",
307
+ "slug": "interruptionlistener",
308
+ "docs": "Listener called when the audio session is interrupted or ends.",
309
+ "types": [
310
+ {
311
+ "text": "(type: InterruptionTypes): void",
312
+ "complexTypes": [
313
+ "InterruptionTypes"
314
+ ]
315
+ }
316
+ ]
317
+ }
318
+ ],
319
+ "pluginConfigs": []
320
+ }
@@ -0,0 +1,85 @@
1
+ import type { PluginListenerHandle } from '@capacitor/core';
2
+ export declare enum RouteChangeReasons {
3
+ NEW_DEVICE_AVAILABLE = "new-device-available",
4
+ OLD_DEVICE_UNAVAILABLE = "old-device-unavailable",
5
+ CATEGORY_CHANGE = "category-change",
6
+ OVERRIDE = "override",
7
+ WAKE_FROM_SLEEP = "wake-from-sleep",
8
+ NO_SUITABLE_ROUTE_FOR_CATEGORY = "no-suitable-route-for-category",
9
+ ROUTE_CONFIGURATION_CHANGE = "route-config-change",
10
+ UNKNOWN = "unknown"
11
+ }
12
+ export declare enum InterruptionTypes {
13
+ BEGAN = "began",
14
+ ENDED = "ended"
15
+ }
16
+ /**
17
+ * Available audio output routes on iOS.
18
+ */
19
+ export declare enum AudioSessionPorts {
20
+ AIR_PLAY = "airplay",
21
+ BLUETOOTH_LE = "bluetooth-le",
22
+ BLUETOOTH_HFP = "bluetooth-hfp",
23
+ BLUETOOTH_A2DP = "bluetooth-a2dp",
24
+ BUILT_IN_SPEAKER = "builtin-speaker",
25
+ BUILT_IN_RECEIVER = "builtin-receiver",
26
+ HDMI = "hdmi",
27
+ HEADPHONES = "headphones",
28
+ LINE_OUT = "line-out"
29
+ }
30
+ /**
31
+ * Result of an output override request.
32
+ */
33
+ export declare type OverrideResult = {
34
+ success: boolean;
35
+ message: string;
36
+ };
37
+ /**
38
+ * Output override type.
39
+ * - `default`: Use the system-selected route.
40
+ * - `speaker`: Force playback through the built-in speaker.
41
+ */
42
+ export declare type OutputOverrideType = 'default' | 'speaker';
43
+ /**
44
+ * Listener called when the audio route changes.
45
+ */
46
+ export declare type RouteChangeListener = (reason: RouteChangeReasons) => void;
47
+ /**
48
+ * Listener called when the audio session is interrupted or ends.
49
+ */
50
+ export declare type InterruptionListener = (type: InterruptionTypes) => void;
51
+ /**
52
+ * iOS-only plugin to query and control the audio session output and listen to
53
+ * route changes and interruptions.
54
+ */
55
+ export interface AudioSessionPlugin {
56
+ /**
57
+ * Get the current active audio output routes.
58
+ *
59
+ * On web and non-iOS platforms, this resolves to an empty array.
60
+ */
61
+ currentOutputs(): Promise<AudioSessionPorts[]>;
62
+ /**
63
+ * Override the current audio output route.
64
+ *
65
+ * Use `speaker` to force playback through the built-in speaker, or
66
+ * `default` to restore the system-selected route.
67
+ *
68
+ * @param type The desired output override type.
69
+ */
70
+ overrideOutput(type: OutputOverrideType): Promise<OverrideResult>;
71
+ /**
72
+ * Listen for audio route changes (e.g. headset connected/disconnected).
73
+ *
74
+ * @param eventName The route change event name.
75
+ * @param listenerFunc Callback invoked with the route change reason.
76
+ */
77
+ addListener(eventName: 'routeChanged', listenerFunc: RouteChangeListener): Promise<PluginListenerHandle>;
78
+ /**
79
+ * Listen for audio session interruptions (e.g. incoming call) and their end.
80
+ *
81
+ * @param eventName The interruption event name.
82
+ * @param listenerFunc Callback invoked with the interruption type.
83
+ */
84
+ addListener(eventName: 'interruption', listenerFunc: InterruptionListener): Promise<PluginListenerHandle>;
85
+ }
@@ -0,0 +1,32 @@
1
+ export var RouteChangeReasons;
2
+ (function (RouteChangeReasons) {
3
+ RouteChangeReasons["NEW_DEVICE_AVAILABLE"] = "new-device-available";
4
+ RouteChangeReasons["OLD_DEVICE_UNAVAILABLE"] = "old-device-unavailable";
5
+ RouteChangeReasons["CATEGORY_CHANGE"] = "category-change";
6
+ RouteChangeReasons["OVERRIDE"] = "override";
7
+ RouteChangeReasons["WAKE_FROM_SLEEP"] = "wake-from-sleep";
8
+ RouteChangeReasons["NO_SUITABLE_ROUTE_FOR_CATEGORY"] = "no-suitable-route-for-category";
9
+ RouteChangeReasons["ROUTE_CONFIGURATION_CHANGE"] = "route-config-change";
10
+ RouteChangeReasons["UNKNOWN"] = "unknown";
11
+ })(RouteChangeReasons || (RouteChangeReasons = {}));
12
+ export var InterruptionTypes;
13
+ (function (InterruptionTypes) {
14
+ InterruptionTypes["BEGAN"] = "began";
15
+ InterruptionTypes["ENDED"] = "ended";
16
+ })(InterruptionTypes || (InterruptionTypes = {}));
17
+ /**
18
+ * Available audio output routes on iOS.
19
+ */
20
+ export var AudioSessionPorts;
21
+ (function (AudioSessionPorts) {
22
+ AudioSessionPorts["AIR_PLAY"] = "airplay";
23
+ AudioSessionPorts["BLUETOOTH_LE"] = "bluetooth-le";
24
+ AudioSessionPorts["BLUETOOTH_HFP"] = "bluetooth-hfp";
25
+ AudioSessionPorts["BLUETOOTH_A2DP"] = "bluetooth-a2dp";
26
+ AudioSessionPorts["BUILT_IN_SPEAKER"] = "builtin-speaker";
27
+ AudioSessionPorts["BUILT_IN_RECEIVER"] = "builtin-receiver";
28
+ AudioSessionPorts["HDMI"] = "hdmi";
29
+ AudioSessionPorts["HEADPHONES"] = "headphones";
30
+ AudioSessionPorts["LINE_OUT"] = "line-out";
31
+ })(AudioSessionPorts || (AudioSessionPorts = {}));
32
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA,MAAM,CAAN,IAAY,kBASX;AATD,WAAY,kBAAkB;IAC5B,mEAA6C,CAAA;IAC7C,uEAAiD,CAAA;IACjD,yDAAmC,CAAA;IACnC,2CAAqB,CAAA;IACrB,yDAAmC,CAAA;IACnC,uFAAiE,CAAA;IACjE,wEAAkD,CAAA;IAClD,yCAAmB,CAAA;AACrB,CAAC,EATW,kBAAkB,KAAlB,kBAAkB,QAS7B;AAED,MAAM,CAAN,IAAY,iBAGX;AAHD,WAAY,iBAAiB;IAC3B,oCAAe,CAAA;IACf,oCAAe,CAAA;AACjB,CAAC,EAHW,iBAAiB,KAAjB,iBAAiB,QAG5B;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,iBAUX;AAVD,WAAY,iBAAiB;IAC3B,yCAAoB,CAAA;IACpB,kDAA6B,CAAA;IAC7B,oDAA+B,CAAA;IAC/B,sDAAiC,CAAA;IACjC,yDAAoC,CAAA;IACpC,2DAAsC,CAAA;IACtC,kCAAa,CAAA;IACb,8CAAyB,CAAA;IACzB,0CAAqB,CAAA;AACvB,CAAC,EAVW,iBAAiB,KAAjB,iBAAiB,QAU5B","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\nexport enum RouteChangeReasons {\n NEW_DEVICE_AVAILABLE = 'new-device-available',\n OLD_DEVICE_UNAVAILABLE = 'old-device-unavailable',\n CATEGORY_CHANGE = 'category-change',\n OVERRIDE = 'override',\n WAKE_FROM_SLEEP = 'wake-from-sleep',\n NO_SUITABLE_ROUTE_FOR_CATEGORY = 'no-suitable-route-for-category',\n ROUTE_CONFIGURATION_CHANGE = 'route-config-change',\n UNKNOWN = 'unknown',\n}\n\nexport enum InterruptionTypes {\n BEGAN = 'began',\n ENDED = 'ended',\n}\n\n/**\n * Available audio output routes on iOS.\n */\nexport enum AudioSessionPorts {\n AIR_PLAY = 'airplay',\n BLUETOOTH_LE = 'bluetooth-le',\n BLUETOOTH_HFP = 'bluetooth-hfp',\n BLUETOOTH_A2DP = 'bluetooth-a2dp',\n BUILT_IN_SPEAKER = 'builtin-speaker',\n BUILT_IN_RECEIVER = 'builtin-receiver',\n HDMI = 'hdmi',\n HEADPHONES = 'headphones',\n LINE_OUT = 'line-out',\n}\n\n/**\n * Result of an output override request.\n */\nexport type OverrideResult = {\n success: boolean;\n message: string;\n};\n\n/**\n * Output override type.\n * - `default`: Use the system-selected route.\n * - `speaker`: Force playback through the built-in speaker.\n */\nexport type OutputOverrideType = 'default' | 'speaker';\n\n/**\n * Listener called when the audio route changes.\n */\nexport type RouteChangeListener = (reason: RouteChangeReasons) => void;\n/**\n * Listener called when the audio session is interrupted or ends.\n */\nexport type InterruptionListener = (type: InterruptionTypes) => void;\n/**\n * iOS-only plugin to query and control the audio session output and listen to\n * route changes and interruptions.\n */\nexport interface AudioSessionPlugin {\n /**\n * Get the current active audio output routes.\n *\n * On web and non-iOS platforms, this resolves to an empty array.\n */\n currentOutputs(): Promise<AudioSessionPorts[]>;\n /**\n * Override the current audio output route.\n *\n * Use `speaker` to force playback through the built-in speaker, or\n * `default` to restore the system-selected route.\n *\n * @param type The desired output override type.\n */\n overrideOutput(type: OutputOverrideType): Promise<OverrideResult>;\n /**\n * Listen for audio route changes (e.g. headset connected/disconnected).\n *\n * @param eventName The route change event name.\n * @param listenerFunc Callback invoked with the route change reason.\n */\n addListener(eventName: 'routeChanged', listenerFunc: RouteChangeListener): Promise<PluginListenerHandle>;\n /**\n * Listen for audio session interruptions (e.g. incoming call) and their end.\n *\n * @param eventName The interruption event name.\n * @param listenerFunc Callback invoked with the interruption type.\n */\n addListener(eventName: 'interruption', listenerFunc: InterruptionListener): Promise<PluginListenerHandle>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { AudioSessionPlugin } from './definitions';
2
+ declare const AudioSession: AudioSessionPlugin;
3
+ export * from './definitions';
4
+ export { AudioSession };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const AudioSession = registerPlugin('AudioSession', {
3
+ web: () => import('./web').then((m) => new m.AudioSessionWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { AudioSession };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,YAAY,GAAG,cAAc,CAAqB,cAAc,EAAE;IACtE,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;CAChE,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { AudioSessionPlugin } from './definitions';\n\nconst AudioSession = registerPlugin<AudioSessionPlugin>('AudioSession', {\n web: () => import('./web').then((m) => new m.AudioSessionWeb()),\n});\n\nexport * from './definitions';\nexport { AudioSession };\n"]}
@@ -0,0 +1,6 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { AudioSessionPlugin, AudioSessionPorts, OutputOverrideType, OverrideResult } from './definitions';
3
+ export declare class AudioSessionWeb extends WebPlugin implements AudioSessionPlugin {
4
+ currentOutputs(): Promise<AudioSessionPorts[]>;
5
+ overrideOutput(type: OutputOverrideType): Promise<OverrideResult>;
6
+ }
@@ -0,0 +1,15 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class AudioSessionWeb extends WebPlugin {
3
+ async currentOutputs() {
4
+ console.log('AudioSessionPlugin.currentOutputs()', 'only available on a iOS device.');
5
+ return [];
6
+ }
7
+ async overrideOutput(type) {
8
+ console.log(`AudioSessionPlugin.currentOutputs(${type})`, 'only available on a iOS device.');
9
+ return {
10
+ success: false,
11
+ message: '',
12
+ };
13
+ }
14
+ }
15
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC5C,KAAK,CAAC,cAAc;QAClB,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,iCAAiC,CAAC,CAAC;QAEtF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,IAAwB;QAC3C,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,GAAG,EAAE,iCAAiC,CAAC,CAAC;QAE7F,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,EAAE;SACZ,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type { AudioSessionPlugin, AudioSessionPorts, OutputOverrideType, OverrideResult } from './definitions';\n\nexport class AudioSessionWeb extends WebPlugin implements AudioSessionPlugin {\n async currentOutputs(): Promise<AudioSessionPorts[]> {\n console.log('AudioSessionPlugin.currentOutputs()', 'only available on a iOS device.');\n\n return [];\n }\n\n async overrideOutput(type: OutputOverrideType): Promise<OverrideResult> {\n console.log(`AudioSessionPlugin.currentOutputs(${type})`, 'only available on a iOS device.');\n\n return {\n success: false,\n message: '',\n };\n }\n}\n"]}
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ exports.RouteChangeReasons = void 0;
6
+ (function (RouteChangeReasons) {
7
+ RouteChangeReasons["NEW_DEVICE_AVAILABLE"] = "new-device-available";
8
+ RouteChangeReasons["OLD_DEVICE_UNAVAILABLE"] = "old-device-unavailable";
9
+ RouteChangeReasons["CATEGORY_CHANGE"] = "category-change";
10
+ RouteChangeReasons["OVERRIDE"] = "override";
11
+ RouteChangeReasons["WAKE_FROM_SLEEP"] = "wake-from-sleep";
12
+ RouteChangeReasons["NO_SUITABLE_ROUTE_FOR_CATEGORY"] = "no-suitable-route-for-category";
13
+ RouteChangeReasons["ROUTE_CONFIGURATION_CHANGE"] = "route-config-change";
14
+ RouteChangeReasons["UNKNOWN"] = "unknown";
15
+ })(exports.RouteChangeReasons || (exports.RouteChangeReasons = {}));
16
+ exports.InterruptionTypes = void 0;
17
+ (function (InterruptionTypes) {
18
+ InterruptionTypes["BEGAN"] = "began";
19
+ InterruptionTypes["ENDED"] = "ended";
20
+ })(exports.InterruptionTypes || (exports.InterruptionTypes = {}));
21
+ /**
22
+ * Available audio output routes on iOS.
23
+ */
24
+ exports.AudioSessionPorts = void 0;
25
+ (function (AudioSessionPorts) {
26
+ AudioSessionPorts["AIR_PLAY"] = "airplay";
27
+ AudioSessionPorts["BLUETOOTH_LE"] = "bluetooth-le";
28
+ AudioSessionPorts["BLUETOOTH_HFP"] = "bluetooth-hfp";
29
+ AudioSessionPorts["BLUETOOTH_A2DP"] = "bluetooth-a2dp";
30
+ AudioSessionPorts["BUILT_IN_SPEAKER"] = "builtin-speaker";
31
+ AudioSessionPorts["BUILT_IN_RECEIVER"] = "builtin-receiver";
32
+ AudioSessionPorts["HDMI"] = "hdmi";
33
+ AudioSessionPorts["HEADPHONES"] = "headphones";
34
+ AudioSessionPorts["LINE_OUT"] = "line-out";
35
+ })(exports.AudioSessionPorts || (exports.AudioSessionPorts = {}));
36
+
37
+ const AudioSession = core.registerPlugin('AudioSession', {
38
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.AudioSessionWeb()),
39
+ });
40
+
41
+ class AudioSessionWeb extends core.WebPlugin {
42
+ async currentOutputs() {
43
+ console.log('AudioSessionPlugin.currentOutputs()', 'only available on a iOS device.');
44
+ return [];
45
+ }
46
+ async overrideOutput(type) {
47
+ console.log(`AudioSessionPlugin.currentOutputs(${type})`, 'only available on a iOS device.');
48
+ return {
49
+ success: false,
50
+ message: '',
51
+ };
52
+ }
53
+ }
54
+
55
+ var web = /*#__PURE__*/Object.freeze({
56
+ __proto__: null,
57
+ AudioSessionWeb: AudioSessionWeb
58
+ });
59
+
60
+ exports.AudioSession = AudioSession;
61
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var RouteChangeReasons;\n(function (RouteChangeReasons) {\n RouteChangeReasons[\"NEW_DEVICE_AVAILABLE\"] = \"new-device-available\";\n RouteChangeReasons[\"OLD_DEVICE_UNAVAILABLE\"] = \"old-device-unavailable\";\n RouteChangeReasons[\"CATEGORY_CHANGE\"] = \"category-change\";\n RouteChangeReasons[\"OVERRIDE\"] = \"override\";\n RouteChangeReasons[\"WAKE_FROM_SLEEP\"] = \"wake-from-sleep\";\n RouteChangeReasons[\"NO_SUITABLE_ROUTE_FOR_CATEGORY\"] = \"no-suitable-route-for-category\";\n RouteChangeReasons[\"ROUTE_CONFIGURATION_CHANGE\"] = \"route-config-change\";\n RouteChangeReasons[\"UNKNOWN\"] = \"unknown\";\n})(RouteChangeReasons || (RouteChangeReasons = {}));\nexport var InterruptionTypes;\n(function (InterruptionTypes) {\n InterruptionTypes[\"BEGAN\"] = \"began\";\n InterruptionTypes[\"ENDED\"] = \"ended\";\n})(InterruptionTypes || (InterruptionTypes = {}));\n/**\n * Available audio output routes on iOS.\n */\nexport var AudioSessionPorts;\n(function (AudioSessionPorts) {\n AudioSessionPorts[\"AIR_PLAY\"] = \"airplay\";\n AudioSessionPorts[\"BLUETOOTH_LE\"] = \"bluetooth-le\";\n AudioSessionPorts[\"BLUETOOTH_HFP\"] = \"bluetooth-hfp\";\n AudioSessionPorts[\"BLUETOOTH_A2DP\"] = \"bluetooth-a2dp\";\n AudioSessionPorts[\"BUILT_IN_SPEAKER\"] = \"builtin-speaker\";\n AudioSessionPorts[\"BUILT_IN_RECEIVER\"] = \"builtin-receiver\";\n AudioSessionPorts[\"HDMI\"] = \"hdmi\";\n AudioSessionPorts[\"HEADPHONES\"] = \"headphones\";\n AudioSessionPorts[\"LINE_OUT\"] = \"line-out\";\n})(AudioSessionPorts || (AudioSessionPorts = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst AudioSession = registerPlugin('AudioSession', {\n web: () => import('./web').then((m) => new m.AudioSessionWeb()),\n});\nexport * from './definitions';\nexport { AudioSession };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class AudioSessionWeb extends WebPlugin {\n async currentOutputs() {\n console.log('AudioSessionPlugin.currentOutputs()', 'only available on a iOS device.');\n return [];\n }\n async overrideOutput(type) {\n console.log(`AudioSessionPlugin.currentOutputs(${type})`, 'only available on a iOS device.');\n return {\n success: false,\n message: '',\n };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["RouteChangeReasons","InterruptionTypes","AudioSessionPorts","registerPlugin","WebPlugin"],"mappings":";;;;AAAWA;AACX,CAAC,UAAU,kBAAkB,EAAE;AAC/B,IAAI,kBAAkB,CAAC,sBAAsB,CAAC,GAAG,sBAAsB;AACvE,IAAI,kBAAkB,CAAC,wBAAwB,CAAC,GAAG,wBAAwB;AAC3E,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;AAC7D,IAAI,kBAAkB,CAAC,UAAU,CAAC,GAAG,UAAU;AAC/C,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;AAC7D,IAAI,kBAAkB,CAAC,gCAAgC,CAAC,GAAG,gCAAgC;AAC3F,IAAI,kBAAkB,CAAC,4BAA4B,CAAC,GAAG,qBAAqB;AAC5E,IAAI,kBAAkB,CAAC,SAAS,CAAC,GAAG,SAAS;AAC7C,CAAC,EAAEA,0BAAkB,KAAKA,0BAAkB,GAAG,EAAE,CAAC,CAAC;AACxCC;AACX,CAAC,UAAU,iBAAiB,EAAE;AAC9B,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,OAAO;AACxC,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,OAAO;AACxC,CAAC,EAAEA,yBAAiB,KAAKA,yBAAiB,GAAG,EAAE,CAAC,CAAC;AACjD;AACA;AACA;AACWC;AACX,CAAC,UAAU,iBAAiB,EAAE;AAC9B,IAAI,iBAAiB,CAAC,UAAU,CAAC,GAAG,SAAS;AAC7C,IAAI,iBAAiB,CAAC,cAAc,CAAC,GAAG,cAAc;AACtD,IAAI,iBAAiB,CAAC,eAAe,CAAC,GAAG,eAAe;AACxD,IAAI,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,gBAAgB;AAC1D,IAAI,iBAAiB,CAAC,kBAAkB,CAAC,GAAG,iBAAiB;AAC7D,IAAI,iBAAiB,CAAC,mBAAmB,CAAC,GAAG,kBAAkB;AAC/D,IAAI,iBAAiB,CAAC,MAAM,CAAC,GAAG,MAAM;AACtC,IAAI,iBAAiB,CAAC,YAAY,CAAC,GAAG,YAAY;AAClD,IAAI,iBAAiB,CAAC,UAAU,CAAC,GAAG,UAAU;AAC9C,CAAC,EAAEA,yBAAiB,KAAKA,yBAAiB,GAAG,EAAE,CAAC,CAAC;;AC7B5C,MAAC,YAAY,GAAGC,mBAAc,CAAC,cAAc,EAAE;AACpD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;AACnE,CAAC;;ACFM,MAAM,eAAe,SAASC,cAAS,CAAC;AAC/C,IAAI,MAAM,cAAc,GAAG;AAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,iCAAiC,CAAC;AAC7F,QAAQ,OAAO,EAAE;AACjB,IAAI;AACJ,IAAI,MAAM,cAAc,CAAC,IAAI,EAAE;AAC/B,QAAQ,OAAO,CAAC,GAAG,CAAC,CAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,iCAAiC,CAAC;AACpG,QAAQ,OAAO;AACf,YAAY,OAAO,EAAE,KAAK;AAC1B,YAAY,OAAO,EAAE,EAAE;AACvB,SAAS;AACT,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,64 @@
1
+ var capacitorAudioSession = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ exports.RouteChangeReasons = void 0;
5
+ (function (RouteChangeReasons) {
6
+ RouteChangeReasons["NEW_DEVICE_AVAILABLE"] = "new-device-available";
7
+ RouteChangeReasons["OLD_DEVICE_UNAVAILABLE"] = "old-device-unavailable";
8
+ RouteChangeReasons["CATEGORY_CHANGE"] = "category-change";
9
+ RouteChangeReasons["OVERRIDE"] = "override";
10
+ RouteChangeReasons["WAKE_FROM_SLEEP"] = "wake-from-sleep";
11
+ RouteChangeReasons["NO_SUITABLE_ROUTE_FOR_CATEGORY"] = "no-suitable-route-for-category";
12
+ RouteChangeReasons["ROUTE_CONFIGURATION_CHANGE"] = "route-config-change";
13
+ RouteChangeReasons["UNKNOWN"] = "unknown";
14
+ })(exports.RouteChangeReasons || (exports.RouteChangeReasons = {}));
15
+ exports.InterruptionTypes = void 0;
16
+ (function (InterruptionTypes) {
17
+ InterruptionTypes["BEGAN"] = "began";
18
+ InterruptionTypes["ENDED"] = "ended";
19
+ })(exports.InterruptionTypes || (exports.InterruptionTypes = {}));
20
+ /**
21
+ * Available audio output routes on iOS.
22
+ */
23
+ exports.AudioSessionPorts = void 0;
24
+ (function (AudioSessionPorts) {
25
+ AudioSessionPorts["AIR_PLAY"] = "airplay";
26
+ AudioSessionPorts["BLUETOOTH_LE"] = "bluetooth-le";
27
+ AudioSessionPorts["BLUETOOTH_HFP"] = "bluetooth-hfp";
28
+ AudioSessionPorts["BLUETOOTH_A2DP"] = "bluetooth-a2dp";
29
+ AudioSessionPorts["BUILT_IN_SPEAKER"] = "builtin-speaker";
30
+ AudioSessionPorts["BUILT_IN_RECEIVER"] = "builtin-receiver";
31
+ AudioSessionPorts["HDMI"] = "hdmi";
32
+ AudioSessionPorts["HEADPHONES"] = "headphones";
33
+ AudioSessionPorts["LINE_OUT"] = "line-out";
34
+ })(exports.AudioSessionPorts || (exports.AudioSessionPorts = {}));
35
+
36
+ const AudioSession = core.registerPlugin('AudioSession', {
37
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.AudioSessionWeb()),
38
+ });
39
+
40
+ class AudioSessionWeb extends core.WebPlugin {
41
+ async currentOutputs() {
42
+ console.log('AudioSessionPlugin.currentOutputs()', 'only available on a iOS device.');
43
+ return [];
44
+ }
45
+ async overrideOutput(type) {
46
+ console.log(`AudioSessionPlugin.currentOutputs(${type})`, 'only available on a iOS device.');
47
+ return {
48
+ success: false,
49
+ message: '',
50
+ };
51
+ }
52
+ }
53
+
54
+ var web = /*#__PURE__*/Object.freeze({
55
+ __proto__: null,
56
+ AudioSessionWeb: AudioSessionWeb
57
+ });
58
+
59
+ exports.AudioSession = AudioSession;
60
+
61
+ return exports;
62
+
63
+ })({}, capacitorExports);
64
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["export var RouteChangeReasons;\n(function (RouteChangeReasons) {\n RouteChangeReasons[\"NEW_DEVICE_AVAILABLE\"] = \"new-device-available\";\n RouteChangeReasons[\"OLD_DEVICE_UNAVAILABLE\"] = \"old-device-unavailable\";\n RouteChangeReasons[\"CATEGORY_CHANGE\"] = \"category-change\";\n RouteChangeReasons[\"OVERRIDE\"] = \"override\";\n RouteChangeReasons[\"WAKE_FROM_SLEEP\"] = \"wake-from-sleep\";\n RouteChangeReasons[\"NO_SUITABLE_ROUTE_FOR_CATEGORY\"] = \"no-suitable-route-for-category\";\n RouteChangeReasons[\"ROUTE_CONFIGURATION_CHANGE\"] = \"route-config-change\";\n RouteChangeReasons[\"UNKNOWN\"] = \"unknown\";\n})(RouteChangeReasons || (RouteChangeReasons = {}));\nexport var InterruptionTypes;\n(function (InterruptionTypes) {\n InterruptionTypes[\"BEGAN\"] = \"began\";\n InterruptionTypes[\"ENDED\"] = \"ended\";\n})(InterruptionTypes || (InterruptionTypes = {}));\n/**\n * Available audio output routes on iOS.\n */\nexport var AudioSessionPorts;\n(function (AudioSessionPorts) {\n AudioSessionPorts[\"AIR_PLAY\"] = \"airplay\";\n AudioSessionPorts[\"BLUETOOTH_LE\"] = \"bluetooth-le\";\n AudioSessionPorts[\"BLUETOOTH_HFP\"] = \"bluetooth-hfp\";\n AudioSessionPorts[\"BLUETOOTH_A2DP\"] = \"bluetooth-a2dp\";\n AudioSessionPorts[\"BUILT_IN_SPEAKER\"] = \"builtin-speaker\";\n AudioSessionPorts[\"BUILT_IN_RECEIVER\"] = \"builtin-receiver\";\n AudioSessionPorts[\"HDMI\"] = \"hdmi\";\n AudioSessionPorts[\"HEADPHONES\"] = \"headphones\";\n AudioSessionPorts[\"LINE_OUT\"] = \"line-out\";\n})(AudioSessionPorts || (AudioSessionPorts = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst AudioSession = registerPlugin('AudioSession', {\n web: () => import('./web').then((m) => new m.AudioSessionWeb()),\n});\nexport * from './definitions';\nexport { AudioSession };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class AudioSessionWeb extends WebPlugin {\n async currentOutputs() {\n console.log('AudioSessionPlugin.currentOutputs()', 'only available on a iOS device.');\n return [];\n }\n async overrideOutput(type) {\n console.log(`AudioSessionPlugin.currentOutputs(${type})`, 'only available on a iOS device.');\n return {\n success: false,\n message: '',\n };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["RouteChangeReasons","InterruptionTypes","AudioSessionPorts","registerPlugin","WebPlugin"],"mappings":";;;AAAWA;IACX,CAAC,UAAU,kBAAkB,EAAE;IAC/B,IAAI,kBAAkB,CAAC,sBAAsB,CAAC,GAAG,sBAAsB;IACvE,IAAI,kBAAkB,CAAC,wBAAwB,CAAC,GAAG,wBAAwB;IAC3E,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;IAC7D,IAAI,kBAAkB,CAAC,UAAU,CAAC,GAAG,UAAU;IAC/C,IAAI,kBAAkB,CAAC,iBAAiB,CAAC,GAAG,iBAAiB;IAC7D,IAAI,kBAAkB,CAAC,gCAAgC,CAAC,GAAG,gCAAgC;IAC3F,IAAI,kBAAkB,CAAC,4BAA4B,CAAC,GAAG,qBAAqB;IAC5E,IAAI,kBAAkB,CAAC,SAAS,CAAC,GAAG,SAAS;IAC7C,CAAC,EAAEA,0BAAkB,KAAKA,0BAAkB,GAAG,EAAE,CAAC,CAAC;AACxCC;IACX,CAAC,UAAU,iBAAiB,EAAE;IAC9B,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,OAAO;IACxC,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,OAAO;IACxC,CAAC,EAAEA,yBAAiB,KAAKA,yBAAiB,GAAG,EAAE,CAAC,CAAC;IACjD;IACA;IACA;AACWC;IACX,CAAC,UAAU,iBAAiB,EAAE;IAC9B,IAAI,iBAAiB,CAAC,UAAU,CAAC,GAAG,SAAS;IAC7C,IAAI,iBAAiB,CAAC,cAAc,CAAC,GAAG,cAAc;IACtD,IAAI,iBAAiB,CAAC,eAAe,CAAC,GAAG,eAAe;IACxD,IAAI,iBAAiB,CAAC,gBAAgB,CAAC,GAAG,gBAAgB;IAC1D,IAAI,iBAAiB,CAAC,kBAAkB,CAAC,GAAG,iBAAiB;IAC7D,IAAI,iBAAiB,CAAC,mBAAmB,CAAC,GAAG,kBAAkB;IAC/D,IAAI,iBAAiB,CAAC,MAAM,CAAC,GAAG,MAAM;IACtC,IAAI,iBAAiB,CAAC,YAAY,CAAC,GAAG,YAAY;IAClD,IAAI,iBAAiB,CAAC,UAAU,CAAC,GAAG,UAAU;IAC9C,CAAC,EAAEA,yBAAiB,KAAKA,yBAAiB,GAAG,EAAE,CAAC,CAAC;;AC7B5C,UAAC,YAAY,GAAGC,mBAAc,CAAC,cAAc,EAAE;IACpD,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,eAAe,EAAE,CAAC;IACnE,CAAC;;ICFM,MAAM,eAAe,SAASC,cAAS,CAAC;IAC/C,IAAI,MAAM,cAAc,GAAG;IAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,qCAAqC,EAAE,iCAAiC,CAAC;IAC7F,QAAQ,OAAO,EAAE;IACjB,IAAI;IACJ,IAAI,MAAM,cAAc,CAAC,IAAI,EAAE;IAC/B,QAAQ,OAAO,CAAC,GAAG,CAAC,CAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,iCAAiC,CAAC;IACpG,QAAQ,OAAO;IACf,YAAY,OAAO,EAAE,KAAK;IAC1B,YAAY,OAAO,EAAE,EAAE;IACvB,SAAS;IACT,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,136 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import AVKit
4
+
5
+ var AudioSessionOverrideTypes: [AVAudioSession.PortOverride: String] = [
6
+ .none: "none",
7
+ .speaker: "speaker"
8
+ ]
9
+
10
+ var AudioSessionRouteChangeReasons: [AVAudioSession.RouteChangeReason: String] = [
11
+ .newDeviceAvailable: "new-device-available",
12
+ .oldDeviceUnavailable: "old-device-unavailable",
13
+ .categoryChange: "category-change",
14
+ .override: "override",
15
+ .wakeFromSleep: "wake-from-sleep",
16
+ .noSuitableRouteForCategory: "no-suitable-route-for-category",
17
+ .routeConfigurationChange: "route-config-change",
18
+ .unknown: "unknown"
19
+ ]
20
+
21
+ var AudioSessionInterruptionTypes: [AVAudioSession.InterruptionType: String] = [
22
+ .began: "began",
23
+ .ended: "ended"
24
+ ]
25
+
26
+ var AudioSessionPorts: [AVAudioSession.Port: String] = [
27
+ .airPlay: "airplay",
28
+ .bluetoothLE: "bluetooth-le",
29
+ .bluetoothHFP: "bluetooth-hfp",
30
+ .bluetoothA2DP: "bluetooth-a2dp",
31
+ .builtInSpeaker: "builtin-speaker",
32
+ .builtInReceiver: "builtin-receiver",
33
+ .HDMI: "hdmi",
34
+ .headphones: "headphones",
35
+ .lineOut: "line-out"
36
+ ]
37
+
38
+ public typealias AudioSessionRouteChangeObserver = (String) -> Void
39
+ public typealias AudioSessionInterruptionObserver = (String) -> Void
40
+ public typealias AudioSessionOverrideCallback = (Bool, String?, Bool?) -> Void
41
+
42
+ public class AudioSession: NSObject {
43
+
44
+ var routeChangeObserver: AudioSessionRouteChangeObserver?
45
+ var interruptionObserver: AudioSessionInterruptionObserver?
46
+
47
+ var currentOverride: String?
48
+
49
+ public func load() {
50
+ let nc = NotificationCenter.default
51
+
52
+ nc.addObserver(self,
53
+ selector: #selector(self.handleRouteChange),
54
+ name: AVAudioSession.routeChangeNotification,
55
+ object: nil)
56
+
57
+ nc.addObserver(self,
58
+ selector: #selector(self.handleInterruption),
59
+ name: AVAudioSession.interruptionNotification,
60
+ object: AVAudioSession.sharedInstance)
61
+ }
62
+
63
+ // EVENTS
64
+
65
+ @objc func handleRouteChange(notification: Notification) {
66
+ guard let userInfo = notification.userInfo,
67
+ let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
68
+ let reasonType = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else { return }
69
+ let readableReason = AudioSessionRouteChangeReasons[reasonType] ?? "unknown"
70
+
71
+ CAPLog.print("AudioSession.handleRouteChange() changed to \(readableReason)")
72
+
73
+ self.routeChangeObserver?(readableReason)
74
+ }
75
+
76
+ @objc func handleInterruption(notification: Notification) {
77
+ guard let userInfo = notification.userInfo,
78
+ let interruptValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
79
+ let interruptType = AVAudioSession.InterruptionType(rawValue: interruptValue) else { return }
80
+ let readableInterrupt = AudioSessionInterruptionTypes[interruptType] ?? "unknown"
81
+
82
+ CAPLog.print("AudioSession.handleInterruption() interrupted status to \(readableInterrupt)")
83
+
84
+ self.interruptionObserver?(readableInterrupt)
85
+ }
86
+
87
+ // METHODS
88
+
89
+ public func currentOutputs() -> [String?] {
90
+ let outputs = AVAudioSession.sharedInstance().currentRoute.outputs.map({AudioSessionPorts[$0.portType]})
91
+
92
+ return outputs
93
+ }
94
+
95
+ public func overrideOutput(_output: String, _callback: @escaping AudioSessionOverrideCallback) {
96
+ if _output == "unknown" {
97
+ return _callback(false, "No valid output provided...", nil)
98
+ }
99
+
100
+ if self.currentOverride == _output {
101
+ return _callback(true, nil, nil)
102
+ }
103
+
104
+ // make it async, cause in latest IOS it started to take ~1 sec and produce UI thread blocking issues
105
+ DispatchQueue.global(qos: .utility).async {
106
+ let session = AVAudioSession.sharedInstance()
107
+
108
+ // make sure the AVAudioSession is properly configured
109
+ do {
110
+ try session.setActive(true)
111
+ try session.setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.duckOthers)
112
+ } catch {
113
+ CAPLog.print("AudioSession.overrideOutput() error setting sessions settings.")
114
+ _callback(false, "Error setting sessions settings.", true)
115
+ return
116
+ }
117
+
118
+ do {
119
+ if _output == "speaker" {
120
+ try session.overrideOutputAudioPort(.speaker)
121
+ } else {
122
+ try session.overrideOutputAudioPort(.none)
123
+ }
124
+
125
+ self.currentOverride = _output
126
+
127
+ CAPLog.print("currentOverride: " + (self.currentOverride ?? "") + " - " + _output)
128
+
129
+ _callback(true, nil, nil)
130
+ } catch {
131
+ CAPLog.print("AudioSession.overrideOutput() could not override output port.")
132
+ _callback(false, "Could not override output port.", true)
133
+ }
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,53 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc(AudioSessionPlugin)
5
+ public class AudioSessionPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "AudioSessionPlugin"
7
+ public let jsName = "AudioSession"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "currentOutputs", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "overrideOutput", returnType: CAPPluginReturnPromise),
11
+ ]
12
+ private let implementation = AudioSession()
13
+
14
+ override public func load() {
15
+
16
+ implementation.load()
17
+
18
+ implementation.interruptionObserver = { [weak self] interrupt in
19
+ self?.notifyListeners("interruption", data: [
20
+ "type": interrupt
21
+ ])
22
+ }
23
+
24
+ implementation.routeChangeObserver = { [weak self] reason in
25
+ self?.notifyListeners("routeChanged", data: [
26
+ "reason": reason
27
+ ])
28
+ }
29
+ }
30
+
31
+ @objc func currentOutputs(_ call: CAPPluginCall) {
32
+ let outputs = implementation.currentOutputs()
33
+
34
+ call.resolve([
35
+ "outputs": outputs
36
+ ])
37
+ }
38
+
39
+ @objc func overrideOutput(_ call: CAPPluginCall) {
40
+ let output = call.getString("type") ?? "unknown"
41
+
42
+ implementation.overrideOutput(_output: output) { (success, message, error) -> Void in
43
+ if error == true {
44
+ call.reject(message ?? "")
45
+ } else {
46
+ call.resolve([
47
+ "success": success,
48
+ "message": message ?? ""
49
+ ])
50
+ }
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import AudioSessionPlugin
3
+
4
+ class SocialLoginTests: XCTestCase {
5
+ func testEcho() {
6
+ // This is an example of a functional test case for a plugin.
7
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
8
+
9
+ let implementation = AudioSessionPlugin()
10
+ let value = "Hello, World!"
11
+ let result = implementation.echo(value)
12
+
13
+ XCTAssertEqual(value, result)
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@capgo/capacitor-audio-session",
3
+ "version": "7.0.2",
4
+ "description": "This capacitor plugin allows iOS applications to get notified audio about interrupts & route changes (for example when a headset is connected), and also query and override the audio device in use.",
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/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "CapgoCapacitorPluginAudioSession.podspec"
17
+ ],
18
+ "author": "Martin Donadieu <martin@capgo.app>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/cap-go/capacitor-plugin-audiosession.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/cap-go/capacitor-plugin-audiosession/issues"
26
+ },
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "native",
31
+ "audio",
32
+ "audio-session"
33
+ ],
34
+ "scripts": {
35
+ "verify": "npm run verify:ios && npm run verify:web",
36
+ "verify:ios": "xcodebuild -scheme CapgoCapacitorPluginAudioSession -destination generic/platform=iOS",
37
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
38
+ "verify:web": "npm run build",
39
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
40
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
41
+ "eslint": "eslint . --ext ts",
42
+ "prettier": "prettier \"**/*.{css,html,ts,js}\"",
43
+ "swiftlint": "node-swiftlint",
44
+ "docgen": "docgen --api AudioSessionPlugin --output-readme README.md --output-json dist/docs.json",
45
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
46
+ "clean": "rimraf ./dist",
47
+ "watch": "tsc --watch",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "devDependencies": {
51
+ "@capacitor/core": "^7.0.0",
52
+ "@capacitor/docgen": "^0.3.0",
53
+ "@capacitor/ios": "^7.0.0",
54
+ "@capacitor/android": "^7.0.0",
55
+ "@ionic/eslint-config": "^0.4.0",
56
+ "@ionic/prettier-config": "^4.0.0",
57
+ "@ionic/swiftlint-config": "^2.0.0",
58
+ "eslint": "^8.57.0",
59
+ "prettier": "^3.4.2",
60
+ "rimraf": "^6.0.1",
61
+ "rollup": "^4.30.1",
62
+ "swiftlint": "^2.0.0",
63
+ "typescript": "~4.1.5",
64
+ "prettier-plugin-java": "^2.6.6"
65
+ },
66
+ "peerDependencies": {
67
+ "@capacitor/core": ">=7.0.0"
68
+ },
69
+ "prettier": "@ionic/prettier-config",
70
+ "swiftlint": "@ionic/swiftlint-config",
71
+ "eslintConfig": {
72
+ "extends": "@ionic/eslint-config/recommended"
73
+ },
74
+ "capacitor": {
75
+ "ios": {
76
+ "src": "ios"
77
+ }
78
+ },
79
+ "np": {
80
+ "yarn": false,
81
+ "tests": false
82
+ }
83
+ }