@capawesome/capacitor-screen-reader 0.0.1

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.
Files changed (41) hide show
  1. package/CapawesomeCapacitorScreenReader.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +245 -0
  5. package/android/build.gradle +58 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/ScreenReader.java +93 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/ScreenReaderPlugin.java +122 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/classes/CustomException.java +20 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/classes/CustomExceptions.java +6 -0
  11. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/classes/events/StateChangeEvent.java +22 -0
  12. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/classes/options/AnnounceOptions.java +34 -0
  13. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/classes/results/IsEnabledResult.java +22 -0
  14. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/interfaces/Callback.java +5 -0
  15. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/interfaces/EmptyCallback.java +5 -0
  16. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/interfaces/NonEmptyResultCallback.java +7 -0
  17. package/android/src/main/java/io/capawesome/capacitorjs/plugins/screenreader/interfaces/Result.java +7 -0
  18. package/android/src/main/res/.gitkeep +0 -0
  19. package/dist/docs.json +222 -0
  20. package/dist/esm/definitions.d.ts +92 -0
  21. package/dist/esm/definitions.js +2 -0
  22. package/dist/esm/definitions.js.map +1 -0
  23. package/dist/esm/index.d.ts +4 -0
  24. package/dist/esm/index.js +7 -0
  25. package/dist/esm/index.js.map +1 -0
  26. package/dist/esm/web.d.ts +11 -0
  27. package/dist/esm/web.js +42 -0
  28. package/dist/esm/web.js.map +1 -0
  29. package/dist/plugin.cjs.js +56 -0
  30. package/dist/plugin.cjs.js.map +1 -0
  31. package/dist/plugin.js +59 -0
  32. package/dist/plugin.js.map +1 -0
  33. package/ios/Plugin/Classes/Events/StateChangeEvent.swift +16 -0
  34. package/ios/Plugin/Classes/Options/AnnounceOptions.swift +15 -0
  35. package/ios/Plugin/Classes/Results/IsEnabledResult.swift +16 -0
  36. package/ios/Plugin/Enums/CustomError.swift +21 -0
  37. package/ios/Plugin/Info.plist +24 -0
  38. package/ios/Plugin/Protocols/Result.swift +5 -0
  39. package/ios/Plugin/ScreenReader.swift +71 -0
  40. package/ios/Plugin/ScreenReaderPlugin.swift +86 -0
  41. package/package.json +95 -0
@@ -0,0 +1,21 @@
1
+ import Foundation
2
+
3
+ enum CustomError: Error {
4
+ case valueMissing
5
+
6
+ var code: String? {
7
+ switch self {
8
+ case .valueMissing:
9
+ return nil
10
+ }
11
+ }
12
+ }
13
+
14
+ extension CustomError: LocalizedError {
15
+ public var errorDescription: String? {
16
+ switch self {
17
+ case .valueMissing:
18
+ return NSLocalizedString("value must be provided.", comment: "valueMissing")
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
@@ -0,0 +1,5 @@
1
+ import Capacitor
2
+
3
+ @objc public protocol Result {
4
+ @objc func toJSObject() -> AnyObject
5
+ }
@@ -0,0 +1,71 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ @objc public class ScreenReader: NSObject {
5
+ private let plugin: ScreenReaderPlugin
6
+
7
+ private var isObserving = false
8
+
9
+ private var lastEnabled = false
10
+
11
+ init(plugin: ScreenReaderPlugin) {
12
+ self.plugin = plugin
13
+ }
14
+
15
+ deinit {
16
+ NotificationCenter.default.removeObserver(self)
17
+ }
18
+
19
+ @objc public func announce(_ options: AnnounceOptions, completion: @escaping (Error?) -> Void) {
20
+ let value = options.value
21
+ DispatchQueue.main.async {
22
+ UIAccessibility.post(notification: .announcement, argument: value)
23
+ completion(nil)
24
+ }
25
+ }
26
+
27
+ @objc public func isEnabled(completion: @escaping (IsEnabledResult?, Error?) -> Void) {
28
+ DispatchQueue.main.async {
29
+ completion(IsEnabledResult(enabled: UIAccessibility.isVoiceOverRunning), nil)
30
+ }
31
+ }
32
+
33
+ func startObserving() {
34
+ DispatchQueue.main.async { [weak self] in
35
+ guard let self = self, !self.isObserving else {
36
+ return
37
+ }
38
+ self.isObserving = true
39
+ self.lastEnabled = UIAccessibility.isVoiceOverRunning
40
+ NotificationCenter.default.addObserver(
41
+ self,
42
+ selector: #selector(self.handleVoiceOverStatusDidChange),
43
+ name: UIAccessibility.voiceOverStatusDidChangeNotification,
44
+ object: nil
45
+ )
46
+ }
47
+ }
48
+
49
+ func stopObserving() {
50
+ DispatchQueue.main.async { [weak self] in
51
+ guard let self = self, self.isObserving else {
52
+ return
53
+ }
54
+ self.isObserving = false
55
+ NotificationCenter.default.removeObserver(
56
+ self,
57
+ name: UIAccessibility.voiceOverStatusDidChangeNotification,
58
+ object: nil
59
+ )
60
+ }
61
+ }
62
+
63
+ @objc private func handleVoiceOverStatusDidChange() {
64
+ let enabled = UIAccessibility.isVoiceOverRunning
65
+ guard enabled != lastEnabled else {
66
+ return
67
+ }
68
+ lastEnabled = enabled
69
+ plugin.notifyStateChangeListeners(StateChangeEvent(enabled: enabled))
70
+ }
71
+ }
@@ -0,0 +1,86 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc(ScreenReaderPlugin)
5
+ public class ScreenReaderPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "ScreenReaderPlugin"
7
+ public let jsName = "ScreenReader"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "announce", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "isEnabled", returnType: CAPPluginReturnPromise)
11
+ ]
12
+
13
+ public static let eventStateChange = "stateChange"
14
+
15
+ public static let tag = "ScreenReaderPlugin"
16
+
17
+ private var implementation: ScreenReader?
18
+
19
+ override public func load() {
20
+ self.implementation = ScreenReader(plugin: self)
21
+ }
22
+
23
+ @objc override public func addListener(_ call: CAPPluginCall) {
24
+ super.addListener(call)
25
+ implementation?.startObserving()
26
+ }
27
+
28
+ @objc func announce(_ call: CAPPluginCall) {
29
+ do {
30
+ let options = try AnnounceOptions(call)
31
+ implementation?.announce(options) { error in
32
+ if let error = error {
33
+ self.rejectCall(call, error)
34
+ return
35
+ }
36
+ self.resolveCall(call)
37
+ }
38
+ } catch {
39
+ rejectCall(call, error)
40
+ }
41
+ }
42
+
43
+ @objc func isEnabled(_ call: CAPPluginCall) {
44
+ implementation?.isEnabled { result, error in
45
+ if let error = error {
46
+ self.rejectCall(call, error)
47
+ return
48
+ }
49
+ self.resolveCall(call, result)
50
+ }
51
+ }
52
+
53
+ public func notifyStateChangeListeners(_ event: StateChangeEvent) {
54
+ self.notifyListeners(Self.eventStateChange, data: event.toJSObject() as? [String: Any])
55
+ }
56
+
57
+ @objc override public func removeAllListeners(_ call: CAPPluginCall) {
58
+ super.removeAllListeners(call)
59
+ implementation?.stopObserving()
60
+ }
61
+
62
+ @objc override public func removeListener(_ call: CAPPluginCall) {
63
+ super.removeListener(call)
64
+ if !hasListeners(Self.eventStateChange) {
65
+ implementation?.stopObserving()
66
+ }
67
+ }
68
+
69
+ private func rejectCall(_ call: CAPPluginCall, _ error: Error) {
70
+ CAPLog.print("[", ScreenReaderPlugin.tag, "] ", error)
71
+ let code = (error as? CustomError)?.code
72
+ call.reject(error.localizedDescription, code)
73
+ }
74
+
75
+ private func resolveCall(_ call: CAPPluginCall) {
76
+ call.resolve()
77
+ }
78
+
79
+ private func resolveCall(_ call: CAPPluginCall, _ result: Result?) {
80
+ if let result = result?.toJSObject() as? JSObject {
81
+ call.resolve(result)
82
+ } else {
83
+ call.resolve()
84
+ }
85
+ }
86
+ }
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@capawesome/capacitor-screen-reader",
3
+ "version": "0.0.1",
4
+ "description": "Capacitor plugin to interact with screen readers.",
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
+ "CapawesomeCapacitorScreenReader.podspec",
15
+ "Package.swift"
16
+ ],
17
+ "author": "Robin Genz <mail@robingenz.dev>",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/capawesome-team/capacitor-plugins.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/capawesome-team/capacitor-plugins/issues"
25
+ },
26
+ "funding": [
27
+ {
28
+ "type": "github",
29
+ "url": "https://github.com/sponsors/capawesome-team/"
30
+ },
31
+ {
32
+ "type": "opencollective",
33
+ "url": "https://opencollective.com/capawesome"
34
+ }
35
+ ],
36
+ "homepage": "https://capawesome.io/docs/sdks/capacitor/screen-reader/",
37
+ "keywords": [
38
+ "capacitor",
39
+ "plugin",
40
+ "native",
41
+ "screen reader",
42
+ "accessibility",
43
+ "voiceover",
44
+ "talkback"
45
+ ],
46
+ "scripts": {
47
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
48
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
49
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
50
+ "verify:web": "npm run build",
51
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
52
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
53
+ "eslint": "eslint . --ext ts",
54
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
55
+ "swiftlint": "node-swiftlint",
56
+ "docgen": "docgen --api ScreenReaderPlugin --output-readme README.md --output-json dist/docs.json",
57
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
58
+ "clean": "rimraf ./dist",
59
+ "watch": "tsc --watch",
60
+ "ios:pod:install": "cd ios && pod install --repo-update && cd ..",
61
+ "ios:spm:install": "cd ios && swift package resolve && cd ..",
62
+ "prepublishOnly": "npm run build"
63
+ },
64
+ "devDependencies": {
65
+ "@capacitor/android": "8.0.0",
66
+ "@capacitor/cli": "8.0.0",
67
+ "@capacitor/core": "8.0.0",
68
+ "@capacitor/docgen": "0.3.1",
69
+ "@capacitor/ios": "8.0.0",
70
+ "@ionic/eslint-config": "0.4.0",
71
+ "eslint": "8.57.0",
72
+ "prettier-plugin-java": "2.6.7",
73
+ "rimraf": "6.1.2",
74
+ "rollup": "4.53.3",
75
+ "swiftlint": "2.0.0",
76
+ "typescript": "5.9.3"
77
+ },
78
+ "peerDependencies": {
79
+ "@capacitor/core": ">=8.0.0"
80
+ },
81
+ "eslintConfig": {
82
+ "extends": "@ionic/eslint-config/recommended"
83
+ },
84
+ "capacitor": {
85
+ "ios": {
86
+ "src": "ios"
87
+ },
88
+ "android": {
89
+ "src": "android"
90
+ }
91
+ },
92
+ "publishConfig": {
93
+ "access": "public"
94
+ }
95
+ }