@elizaos/capacitor-appblocker 1.0.0
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.
- package/ElizaosCapacitorAppblocker.podspec +18 -0
- package/android/build.gradle +51 -0
- package/android/src/main/AndroidManifest.xml +24 -0
- package/android/src/main/java/ai/eliza/plugins/appblocker/AppBlockerForegroundService.kt +345 -0
- package/android/src/main/java/ai/eliza/plugins/appblocker/AppBlockerPlugin.kt +260 -0
- package/android/src/main/java/ai/eliza/plugins/appblocker/AppBlockerStateStore.kt +65 -0
- package/dist/esm/definitions.d.ts +53 -0
- package/dist/esm/definitions.d.ts.map +1 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +14 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +51 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +66 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +69 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/AppBlockerPlugin/AppBlockerPlugin.swift +196 -0
- package/ios/Sources/AppBlockerPlugin/AppBlockerShared.swift +102 -0
- package/ios/Sources/AppBlockerPlugin/FamilyActivityPickerBridge.swift +47 -0
- package/package.json +77 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import FamilyControls
|
|
2
|
+
import Foundation
|
|
3
|
+
import ManagedSettings
|
|
4
|
+
|
|
5
|
+
struct AppBlockerStoredState: Codable {
|
|
6
|
+
let tokenDataArray: [String]
|
|
7
|
+
let endsAtEpochMs: Double?
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum AppBlockerShared {
|
|
11
|
+
static let stateKey = "app_blocker_state_v1"
|
|
12
|
+
static let store = ManagedSettingsStore()
|
|
13
|
+
static let iso8601Formatter = ISO8601DateFormatter()
|
|
14
|
+
|
|
15
|
+
static func serializeToken(_ token: ApplicationToken) -> String? {
|
|
16
|
+
guard let data = try? JSONEncoder().encode(token) else {
|
|
17
|
+
return nil
|
|
18
|
+
}
|
|
19
|
+
return data.base64EncodedString()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static func serializeSelection(_ tokens: Set<ApplicationToken>) -> [String] {
|
|
23
|
+
tokens.compactMap(serializeToken).sorted()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static func deserializeTokens(_ tokenDataArray: [String]) -> Set<ApplicationToken> {
|
|
27
|
+
var tokens = Set<ApplicationToken>()
|
|
28
|
+
for tokenData in tokenDataArray {
|
|
29
|
+
guard let data = Data(base64Encoded: tokenData),
|
|
30
|
+
let token = try? JSONDecoder().decode(ApplicationToken.self, from: data) else {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
tokens.insert(token)
|
|
34
|
+
}
|
|
35
|
+
return tokens
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static func loadState() -> AppBlockerStoredState? {
|
|
39
|
+
guard let data = UserDefaults.standard.data(forKey: stateKey) else {
|
|
40
|
+
return nil
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
guard let state = try? JSONDecoder().decode(AppBlockerStoredState.self, from: data) else {
|
|
44
|
+
UserDefaults.standard.removeObject(forKey: stateKey)
|
|
45
|
+
return nil
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if let endsAtEpochMs = state.endsAtEpochMs,
|
|
49
|
+
endsAtEpochMs <= Date().timeIntervalSince1970 * 1000 {
|
|
50
|
+
UserDefaults.standard.removeObject(forKey: stateKey)
|
|
51
|
+
return nil
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if state.tokenDataArray.isEmpty {
|
|
55
|
+
UserDefaults.standard.removeObject(forKey: stateKey)
|
|
56
|
+
return nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return state
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static func saveState(tokenDataArray: [String], endsAtEpochMs: Double?) {
|
|
63
|
+
let state = AppBlockerStoredState(
|
|
64
|
+
tokenDataArray: tokenDataArray,
|
|
65
|
+
endsAtEpochMs: endsAtEpochMs
|
|
66
|
+
)
|
|
67
|
+
guard let data = try? JSONEncoder().encode(state) else {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
UserDefaults.standard.set(data, forKey: stateKey)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static func clearState() {
|
|
74
|
+
UserDefaults.standard.removeObject(forKey: stateKey)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static func endsAtString(for state: AppBlockerStoredState?) -> String? {
|
|
78
|
+
guard let endsAtEpochMs = state?.endsAtEpochMs else {
|
|
79
|
+
return nil
|
|
80
|
+
}
|
|
81
|
+
return iso8601Formatter.string(from: Date(timeIntervalSince1970: endsAtEpochMs / 1000))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
static func applyShield(tokens: Set<ApplicationToken>) {
|
|
85
|
+
store.shield.applications = tokens
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static func clearShield() {
|
|
89
|
+
store.shield.applications = nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static func startBlock(tokens: Set<ApplicationToken>, endsAtEpochMs: Double? = nil) {
|
|
93
|
+
let tokenDataArray = serializeSelection(tokens)
|
|
94
|
+
saveState(tokenDataArray: tokenDataArray, endsAtEpochMs: endsAtEpochMs)
|
|
95
|
+
applyShield(tokens: tokens)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static func stopBlock() {
|
|
99
|
+
clearState()
|
|
100
|
+
clearShield()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import FamilyControls
|
|
2
|
+
import ManagedSettings
|
|
3
|
+
import SwiftUI
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
@MainActor
|
|
7
|
+
enum FamilyActivityPickerBridge {
|
|
8
|
+
static func present(
|
|
9
|
+
from viewController: UIViewController,
|
|
10
|
+
completion: @escaping ([ManagedSettings.ApplicationToken], Bool) -> Void
|
|
11
|
+
) {
|
|
12
|
+
let pickerController = UIHostingController(
|
|
13
|
+
rootView: PickerWrapper(completion: completion)
|
|
14
|
+
)
|
|
15
|
+
pickerController.modalPresentationStyle = UIModalPresentationStyle.formSheet
|
|
16
|
+
viewController.present(pickerController, animated: true)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private struct PickerWrapper: View {
|
|
21
|
+
@Environment(\.dismiss) private var dismiss
|
|
22
|
+
@State private var selection = FamilyActivitySelection()
|
|
23
|
+
|
|
24
|
+
let completion: ([ManagedSettings.ApplicationToken], Bool) -> Void
|
|
25
|
+
|
|
26
|
+
var body: some View {
|
|
27
|
+
NavigationView {
|
|
28
|
+
FamilyActivityPicker(selection: $selection)
|
|
29
|
+
.navigationTitle("Select Apps")
|
|
30
|
+
.toolbar {
|
|
31
|
+
ToolbarItem(placement: .cancellationAction) {
|
|
32
|
+
Button("Cancel") {
|
|
33
|
+
completion([], true)
|
|
34
|
+
dismiss()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
ToolbarItem(placement: .confirmationAction) {
|
|
38
|
+
Button("Done") {
|
|
39
|
+
completion(Array(selection.applicationTokens), false)
|
|
40
|
+
dismiss()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
.navigationViewStyle(StackNavigationViewStyle())
|
|
46
|
+
}
|
|
47
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elizaos/capacitor-appblocker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Blocks selected apps on Android with Usage Access and an overlay shield, and on iPhone with Family Controls.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"app-blocker",
|
|
7
|
+
"focus",
|
|
8
|
+
"family-controls",
|
|
9
|
+
"usage-stats"
|
|
10
|
+
],
|
|
11
|
+
"main": "./dist/plugin.cjs.js",
|
|
12
|
+
"module": "./dist/esm/index.js",
|
|
13
|
+
"types": "./dist/esm/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/esm/index.d.ts",
|
|
17
|
+
"import": "./dist/esm/index.js",
|
|
18
|
+
"require": "./dist/plugin.cjs.js"
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
21
|
+
},
|
|
22
|
+
"unpkg": "dist/plugin.js",
|
|
23
|
+
"files": [
|
|
24
|
+
"android/src/main/",
|
|
25
|
+
"android/build.gradle",
|
|
26
|
+
"dist/",
|
|
27
|
+
"ios/Sources/",
|
|
28
|
+
"*.podspec"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "npm run clean && tsc && rollup -c rollup.config.mjs",
|
|
32
|
+
"clean": "rimraf ./dist",
|
|
33
|
+
"prepublishOnly": "npm run build"
|
|
34
|
+
},
|
|
35
|
+
"author": "elizaOS",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/elizaOS/eliza.git",
|
|
40
|
+
"directory": "packages/native-plugins/appblocker"
|
|
41
|
+
},
|
|
42
|
+
"capacitor": {
|
|
43
|
+
"ios": {
|
|
44
|
+
"src": "ios",
|
|
45
|
+
"podName": "ElizaosCapacitorAppblocker"
|
|
46
|
+
},
|
|
47
|
+
"android": {
|
|
48
|
+
"src": "android"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@capacitor/cli": "^8.0.0",
|
|
53
|
+
"@capacitor/core": "^8.3.1",
|
|
54
|
+
"rimraf": "^6.0.0",
|
|
55
|
+
"rollup": "^4.60.2",
|
|
56
|
+
"typescript": "^6.0.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"@capacitor/core": "^8.3.1"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"elizaos": {
|
|
65
|
+
"platforms": [
|
|
66
|
+
"browser",
|
|
67
|
+
"android",
|
|
68
|
+
"ios"
|
|
69
|
+
],
|
|
70
|
+
"runtime": "native",
|
|
71
|
+
"platformDetails": {
|
|
72
|
+
"browser": "Not available in browsers.",
|
|
73
|
+
"ios": "Uses Family Controls and Managed Settings to shield selected apps.",
|
|
74
|
+
"android": "Uses Usage Access polling plus a system overlay shield."
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|