@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,65 @@
|
|
|
1
|
+
package ai.eliza.plugins.appblocker
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
|
|
5
|
+
data class SavedAppBlock(
|
|
6
|
+
val packageNames: List<String>,
|
|
7
|
+
val endsAtEpochMs: Long?,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
object AppBlockerStateStore {
|
|
11
|
+
private const val PREFS_NAME = "eliza_app_blocker"
|
|
12
|
+
private const val KEY_PACKAGE_NAMES = "blocked_package_names"
|
|
13
|
+
private const val KEY_ENDS_AT = "ends_at_epoch_ms"
|
|
14
|
+
|
|
15
|
+
private fun prefs(context: Context) =
|
|
16
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
17
|
+
|
|
18
|
+
fun load(context: Context): SavedAppBlock? {
|
|
19
|
+
val preferences = prefs(context)
|
|
20
|
+
val packageNames = preferences.getStringSet(KEY_PACKAGE_NAMES, null)
|
|
21
|
+
?.toList()
|
|
22
|
+
?.sorted()
|
|
23
|
+
?: return null
|
|
24
|
+
if (packageNames.isEmpty()) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
val endsAtEpochMs = if (preferences.contains(KEY_ENDS_AT)) {
|
|
29
|
+
preferences.getLong(KEY_ENDS_AT, 0L)
|
|
30
|
+
} else {
|
|
31
|
+
null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (endsAtEpochMs != null && endsAtEpochMs <= System.currentTimeMillis()) {
|
|
35
|
+
clear(context)
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return SavedAppBlock(
|
|
40
|
+
packageNames = packageNames,
|
|
41
|
+
endsAtEpochMs = endsAtEpochMs,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fun save(context: Context, packageNames: List<String>, endsAtEpochMs: Long?) {
|
|
46
|
+
prefs(context).edit().apply {
|
|
47
|
+
putStringSet(KEY_PACKAGE_NAMES, packageNames.toSet())
|
|
48
|
+
if (endsAtEpochMs != null) {
|
|
49
|
+
putLong(KEY_ENDS_AT, endsAtEpochMs)
|
|
50
|
+
} else {
|
|
51
|
+
remove(KEY_ENDS_AT)
|
|
52
|
+
}
|
|
53
|
+
apply()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fun clear(context: Context) {
|
|
58
|
+
prefs(context).edit().clear().apply()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fun isBlocked(context: Context, packageName: String): Boolean {
|
|
62
|
+
val saved = load(context) ?: return false
|
|
63
|
+
return saved.packageNames.contains(packageName)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type AppBlockerPermissionStatus = "granted" | "denied" | "not-determined" | "not-applicable";
|
|
2
|
+
export interface AppBlockerPermissionResult {
|
|
3
|
+
status: AppBlockerPermissionStatus;
|
|
4
|
+
canRequest: boolean;
|
|
5
|
+
reason?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface InstalledApp {
|
|
8
|
+
packageName: string;
|
|
9
|
+
displayName: string;
|
|
10
|
+
tokenData?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SelectAppsResult {
|
|
13
|
+
apps: InstalledApp[];
|
|
14
|
+
cancelled: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface BlockAppsOptions {
|
|
17
|
+
appTokens?: string[];
|
|
18
|
+
packageNames?: string[];
|
|
19
|
+
durationMinutes?: number | null;
|
|
20
|
+
}
|
|
21
|
+
export interface BlockAppsResult {
|
|
22
|
+
success: boolean;
|
|
23
|
+
endsAt: string | null;
|
|
24
|
+
error?: string;
|
|
25
|
+
blockedCount: number;
|
|
26
|
+
}
|
|
27
|
+
export interface UnblockAppsResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AppBlockerStatus {
|
|
32
|
+
available: boolean;
|
|
33
|
+
active: boolean;
|
|
34
|
+
platform: string;
|
|
35
|
+
engine: "family-controls" | "usage-stats-overlay" | "none";
|
|
36
|
+
blockedCount: number;
|
|
37
|
+
blockedPackageNames: string[];
|
|
38
|
+
endsAt: string | null;
|
|
39
|
+
permissionStatus: AppBlockerPermissionStatus;
|
|
40
|
+
reason?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface AppBlockerPlugin {
|
|
43
|
+
checkPermissions(): Promise<AppBlockerPermissionResult>;
|
|
44
|
+
requestPermissions(): Promise<AppBlockerPermissionResult>;
|
|
45
|
+
getInstalledApps(): Promise<{
|
|
46
|
+
apps: InstalledApp[];
|
|
47
|
+
}>;
|
|
48
|
+
selectApps(): Promise<SelectAppsResult>;
|
|
49
|
+
blockApps(options: BlockAppsOptions): Promise<BlockAppsResult>;
|
|
50
|
+
unblockApps(): Promise<UnblockAppsResult>;
|
|
51
|
+
getStatus(): Promise<AppBlockerStatus>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=definitions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,0BAA0B,GAClC,SAAS,GACT,QAAQ,GACR,gBAAgB,GAChB,gBAAgB,CAAC;AAErB,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,0BAA0B,CAAC;IACnC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,iBAAiB,GAAG,qBAAqB,GAAG,MAAM,CAAC;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gBAAgB,EAAE,0BAA0B,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IACxD,kBAAkB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC1D,gBAAgB,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC,CAAC;IACtD,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/D,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1C,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACxC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,cAAc,eAAe,CAAC;AAK9B,eAAO,MAAM,UAAU,kBAErB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerPlugin } from "@capacitor/core";
|
|
2
|
+
export * from "./definitions";
|
|
3
|
+
const loadWeb = () => import("./web").then((module) => new module.AppBlockerWeb());
|
|
4
|
+
export const AppBlocker = registerPlugin("ElizaAppBlocker", {
|
|
5
|
+
web: loadWeb,
|
|
6
|
+
});
|
|
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,cAAc,eAAe,CAAC;AAE9B,MAAM,OAAO,GAAG,GAAG,EAAE,CACnB,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;AAE/D,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAmB,iBAAiB,EAAE;IAC5E,GAAG,EAAE,OAAO;CACb,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { WebPlugin } from "@capacitor/core";
|
|
2
|
+
import type { AppBlockerPermissionResult, AppBlockerStatus, BlockAppsOptions, BlockAppsResult, SelectAppsResult, UnblockAppsResult } from "./definitions";
|
|
3
|
+
export declare class AppBlockerWeb extends WebPlugin {
|
|
4
|
+
checkPermissions(): Promise<AppBlockerPermissionResult>;
|
|
5
|
+
requestPermissions(): Promise<AppBlockerPermissionResult>;
|
|
6
|
+
getInstalledApps(): Promise<{
|
|
7
|
+
apps: [];
|
|
8
|
+
}>;
|
|
9
|
+
selectApps(): Promise<SelectAppsResult>;
|
|
10
|
+
blockApps(_options: BlockAppsOptions): Promise<BlockAppsResult>;
|
|
11
|
+
unblockApps(): Promise<UnblockAppsResult>;
|
|
12
|
+
getStatus(): Promise<AppBlockerStatus>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=web.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EACV,0BAA0B,EAC1B,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAEvB,qBAAa,aAAc,SAAQ,SAAS;IACpC,gBAAgB,IAAI,OAAO,CAAC,0BAA0B,CAAC;IAQvD,kBAAkB,IAAI,OAAO,CAAC,0BAA0B,CAAC;IAQzD,gBAAgB,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,EAAE,CAAA;KAAE,CAAC;IAIzC,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIvC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAS/D,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAOzC,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAa7C"}
|
package/dist/esm/web.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { WebPlugin } from "@capacitor/core";
|
|
2
|
+
export class AppBlockerWeb extends WebPlugin {
|
|
3
|
+
async checkPermissions() {
|
|
4
|
+
return {
|
|
5
|
+
status: "not-applicable",
|
|
6
|
+
canRequest: false,
|
|
7
|
+
reason: "App blocking is only available on mobile devices.",
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
async requestPermissions() {
|
|
11
|
+
return {
|
|
12
|
+
status: "not-applicable",
|
|
13
|
+
canRequest: false,
|
|
14
|
+
reason: "App blocking is only available on mobile devices.",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async getInstalledApps() {
|
|
18
|
+
return { apps: [] };
|
|
19
|
+
}
|
|
20
|
+
async selectApps() {
|
|
21
|
+
return { apps: [], cancelled: true };
|
|
22
|
+
}
|
|
23
|
+
async blockApps(_options) {
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
endsAt: null,
|
|
27
|
+
error: "App blocking is only available on mobile devices.",
|
|
28
|
+
blockedCount: 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async unblockApps() {
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
error: "App blocking is only available on mobile devices.",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async getStatus() {
|
|
38
|
+
return {
|
|
39
|
+
available: false,
|
|
40
|
+
active: false,
|
|
41
|
+
platform: "web",
|
|
42
|
+
engine: "none",
|
|
43
|
+
blockedCount: 0,
|
|
44
|
+
blockedPackageNames: [],
|
|
45
|
+
endsAt: null,
|
|
46
|
+
permissionStatus: "not-applicable",
|
|
47
|
+
reason: "App blocking is only available on mobile devices.",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# 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;AAU5C,MAAM,OAAO,aAAc,SAAQ,SAAS;IAC1C,KAAK,CAAC,gBAAgB;QACpB,OAAO;YACL,MAAM,EAAE,gBAAgB;YACxB,UAAU,EAAE,KAAK;YACjB,MAAM,EAAE,mDAAmD;SAC5D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,OAAO;YACL,MAAM,EAAE,gBAAgB;YACxB,UAAU,EAAE,KAAK;YACjB,MAAM,EAAE,mDAAmD;SAC5D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAA0B;QACxC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,mDAAmD;YAC1D,YAAY,EAAE,CAAC;SAChB,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO;YACL,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,mDAAmD;SAC3D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS;QACb,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,MAAM;YACd,YAAY,EAAE,CAAC;YACf,mBAAmB,EAAE,EAAE;YACvB,MAAM,EAAE,IAAI;YACZ,gBAAgB,EAAE,gBAAgB;YAClC,MAAM,EAAE,mDAAmD;SAC5D,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@capacitor/core');
|
|
4
|
+
|
|
5
|
+
const loadWeb = () => Promise.resolve().then(function () { return web; }).then((module) => new module.AppBlockerWeb());
|
|
6
|
+
const AppBlocker = core.registerPlugin("ElizaAppBlocker", {
|
|
7
|
+
web: loadWeb,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
class AppBlockerWeb extends core.WebPlugin {
|
|
11
|
+
async checkPermissions() {
|
|
12
|
+
return {
|
|
13
|
+
status: "not-applicable",
|
|
14
|
+
canRequest: false,
|
|
15
|
+
reason: "App blocking is only available on mobile devices.",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async requestPermissions() {
|
|
19
|
+
return {
|
|
20
|
+
status: "not-applicable",
|
|
21
|
+
canRequest: false,
|
|
22
|
+
reason: "App blocking is only available on mobile devices.",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
async getInstalledApps() {
|
|
26
|
+
return { apps: [] };
|
|
27
|
+
}
|
|
28
|
+
async selectApps() {
|
|
29
|
+
return { apps: [], cancelled: true };
|
|
30
|
+
}
|
|
31
|
+
async blockApps(_options) {
|
|
32
|
+
return {
|
|
33
|
+
success: false,
|
|
34
|
+
endsAt: null,
|
|
35
|
+
error: "App blocking is only available on mobile devices.",
|
|
36
|
+
blockedCount: 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async unblockApps() {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
error: "App blocking is only available on mobile devices.",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async getStatus() {
|
|
46
|
+
return {
|
|
47
|
+
available: false,
|
|
48
|
+
active: false,
|
|
49
|
+
platform: "web",
|
|
50
|
+
engine: "none",
|
|
51
|
+
blockedCount: 0,
|
|
52
|
+
blockedPackageNames: [],
|
|
53
|
+
endsAt: null,
|
|
54
|
+
permissionStatus: "not-applicable",
|
|
55
|
+
reason: "App blocking is only available on mobile devices.",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
61
|
+
__proto__: null,
|
|
62
|
+
AppBlockerWeb: AppBlockerWeb
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
exports.AppBlocker = AppBlocker;
|
|
66
|
+
//# sourceMappingURL=plugin.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((module) => new module.AppBlockerWeb());\nexport const AppBlocker = registerPlugin(\"ElizaAppBlocker\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class AppBlockerWeb extends WebPlugin {\n async checkPermissions() {\n return {\n status: \"not-applicable\",\n canRequest: false,\n reason: \"App blocking is only available on mobile devices.\",\n };\n }\n async requestPermissions() {\n return {\n status: \"not-applicable\",\n canRequest: false,\n reason: \"App blocking is only available on mobile devices.\",\n };\n }\n async getInstalledApps() {\n return { apps: [] };\n }\n async selectApps() {\n return { apps: [], cancelled: true };\n }\n async blockApps(_options) {\n return {\n success: false,\n endsAt: null,\n error: \"App blocking is only available on mobile devices.\",\n blockedCount: 0,\n };\n }\n async unblockApps() {\n return {\n success: false,\n error: \"App blocking is only available on mobile devices.\",\n };\n }\n async getStatus() {\n return {\n available: false,\n active: false,\n platform: \"web\",\n engine: \"none\",\n blockedCount: 0,\n blockedPackageNames: [],\n endsAt: null,\n permissionStatus: \"not-applicable\",\n reason: \"App blocking is only available on mobile devices.\",\n };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;AACtE,MAAC,UAAU,GAAGA,mBAAc,CAAC,iBAAiB,EAAE;AAC5D,IAAI,GAAG,EAAE,OAAO;AAChB,CAAC;;ACJM,MAAM,aAAa,SAASC,cAAS,CAAC;AAC7C,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,OAAO;AACf,YAAY,MAAM,EAAE,gBAAgB;AACpC,YAAY,UAAU,EAAE,KAAK;AAC7B,YAAY,MAAM,EAAE,mDAAmD;AACvE,SAAS;AACT,IAAI;AACJ,IAAI,MAAM,kBAAkB,GAAG;AAC/B,QAAQ,OAAO;AACf,YAAY,MAAM,EAAE,gBAAgB;AACpC,YAAY,UAAU,EAAE,KAAK;AAC7B,YAAY,MAAM,EAAE,mDAAmD;AACvE,SAAS;AACT,IAAI;AACJ,IAAI,MAAM,gBAAgB,GAAG;AAC7B,QAAQ,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;AAC3B,IAAI;AACJ,IAAI,MAAM,UAAU,GAAG;AACvB,QAAQ,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;AAC5C,IAAI;AACJ,IAAI,MAAM,SAAS,CAAC,QAAQ,EAAE;AAC9B,QAAQ,OAAO;AACf,YAAY,OAAO,EAAE,KAAK;AAC1B,YAAY,MAAM,EAAE,IAAI;AACxB,YAAY,KAAK,EAAE,mDAAmD;AACtE,YAAY,YAAY,EAAE,CAAC;AAC3B,SAAS;AACT,IAAI;AACJ,IAAI,MAAM,WAAW,GAAG;AACxB,QAAQ,OAAO;AACf,YAAY,OAAO,EAAE,KAAK;AAC1B,YAAY,KAAK,EAAE,mDAAmD;AACtE,SAAS;AACT,IAAI;AACJ,IAAI,MAAM,SAAS,GAAG;AACtB,QAAQ,OAAO;AACf,YAAY,SAAS,EAAE,KAAK;AAC5B,YAAY,MAAM,EAAE,KAAK;AACzB,YAAY,QAAQ,EAAE,KAAK;AAC3B,YAAY,MAAM,EAAE,MAAM;AAC1B,YAAY,YAAY,EAAE,CAAC;AAC3B,YAAY,mBAAmB,EAAE,EAAE;AACnC,YAAY,MAAM,EAAE,IAAI;AACxB,YAAY,gBAAgB,EAAE,gBAAgB;AAC9C,YAAY,MAAM,EAAE,mDAAmD;AACvE,SAAS;AACT,IAAI;AACJ;;;;;;;;;"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
var capacitorAppBlocker = (function (exports, core) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const loadWeb = () => Promise.resolve().then(function () { return web; }).then((module) => new module.AppBlockerWeb());
|
|
5
|
+
const AppBlocker = core.registerPlugin("ElizaAppBlocker", {
|
|
6
|
+
web: loadWeb,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
class AppBlockerWeb extends core.WebPlugin {
|
|
10
|
+
async checkPermissions() {
|
|
11
|
+
return {
|
|
12
|
+
status: "not-applicable",
|
|
13
|
+
canRequest: false,
|
|
14
|
+
reason: "App blocking is only available on mobile devices.",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async requestPermissions() {
|
|
18
|
+
return {
|
|
19
|
+
status: "not-applicable",
|
|
20
|
+
canRequest: false,
|
|
21
|
+
reason: "App blocking is only available on mobile devices.",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async getInstalledApps() {
|
|
25
|
+
return { apps: [] };
|
|
26
|
+
}
|
|
27
|
+
async selectApps() {
|
|
28
|
+
return { apps: [], cancelled: true };
|
|
29
|
+
}
|
|
30
|
+
async blockApps(_options) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
endsAt: null,
|
|
34
|
+
error: "App blocking is only available on mobile devices.",
|
|
35
|
+
blockedCount: 0,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async unblockApps() {
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
error: "App blocking is only available on mobile devices.",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async getStatus() {
|
|
45
|
+
return {
|
|
46
|
+
available: false,
|
|
47
|
+
active: false,
|
|
48
|
+
platform: "web",
|
|
49
|
+
engine: "none",
|
|
50
|
+
blockedCount: 0,
|
|
51
|
+
blockedPackageNames: [],
|
|
52
|
+
endsAt: null,
|
|
53
|
+
permissionStatus: "not-applicable",
|
|
54
|
+
reason: "App blocking is only available on mobile devices.",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var web = /*#__PURE__*/Object.freeze({
|
|
60
|
+
__proto__: null,
|
|
61
|
+
AppBlockerWeb: AppBlockerWeb
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
exports.AppBlocker = AppBlocker;
|
|
65
|
+
|
|
66
|
+
return exports;
|
|
67
|
+
|
|
68
|
+
})({}, capacitorExports);
|
|
69
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from \"@capacitor/core\";\nexport * from \"./definitions\";\nconst loadWeb = () => import(\"./web\").then((module) => new module.AppBlockerWeb());\nexport const AppBlocker = registerPlugin(\"ElizaAppBlocker\", {\n web: loadWeb,\n});\n//# sourceMappingURL=index.js.map","import { WebPlugin } from \"@capacitor/core\";\nexport class AppBlockerWeb extends WebPlugin {\n async checkPermissions() {\n return {\n status: \"not-applicable\",\n canRequest: false,\n reason: \"App blocking is only available on mobile devices.\",\n };\n }\n async requestPermissions() {\n return {\n status: \"not-applicable\",\n canRequest: false,\n reason: \"App blocking is only available on mobile devices.\",\n };\n }\n async getInstalledApps() {\n return { apps: [] };\n }\n async selectApps() {\n return { apps: [], cancelled: true };\n }\n async blockApps(_options) {\n return {\n success: false,\n endsAt: null,\n error: \"App blocking is only available on mobile devices.\",\n blockedCount: 0,\n };\n }\n async unblockApps() {\n return {\n success: false,\n error: \"App blocking is only available on mobile devices.\",\n };\n }\n async getStatus() {\n return {\n available: false,\n active: false,\n platform: \"web\",\n engine: \"none\",\n blockedCount: 0,\n blockedPackageNames: [],\n endsAt: null,\n permissionStatus: \"not-applicable\",\n reason: \"App blocking is only available on mobile devices.\",\n };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;IAEA,MAAM,OAAO,GAAG,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;AACtE,UAAC,UAAU,GAAGA,mBAAc,CAAC,iBAAiB,EAAE;IAC5D,IAAI,GAAG,EAAE,OAAO;IAChB,CAAC;;ICJM,MAAM,aAAa,SAASC,cAAS,CAAC;IAC7C,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,OAAO;IACf,YAAY,MAAM,EAAE,gBAAgB;IACpC,YAAY,UAAU,EAAE,KAAK;IAC7B,YAAY,MAAM,EAAE,mDAAmD;IACvE,SAAS;IACT,IAAI;IACJ,IAAI,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,OAAO;IACf,YAAY,MAAM,EAAE,gBAAgB;IACpC,YAAY,UAAU,EAAE,KAAK;IAC7B,YAAY,MAAM,EAAE,mDAAmD;IACvE,SAAS;IACT,IAAI;IACJ,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;IAC3B,IAAI;IACJ,IAAI,MAAM,UAAU,GAAG;IACvB,QAAQ,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;IAC5C,IAAI;IACJ,IAAI,MAAM,SAAS,CAAC,QAAQ,EAAE;IAC9B,QAAQ,OAAO;IACf,YAAY,OAAO,EAAE,KAAK;IAC1B,YAAY,MAAM,EAAE,IAAI;IACxB,YAAY,KAAK,EAAE,mDAAmD;IACtE,YAAY,YAAY,EAAE,CAAC;IAC3B,SAAS;IACT,IAAI;IACJ,IAAI,MAAM,WAAW,GAAG;IACxB,QAAQ,OAAO;IACf,YAAY,OAAO,EAAE,KAAK;IAC1B,YAAY,KAAK,EAAE,mDAAmD;IACtE,SAAS;IACT,IAAI;IACJ,IAAI,MAAM,SAAS,GAAG;IACtB,QAAQ,OAAO;IACf,YAAY,SAAS,EAAE,KAAK;IAC5B,YAAY,MAAM,EAAE,KAAK;IACzB,YAAY,QAAQ,EAAE,KAAK;IAC3B,YAAY,MAAM,EAAE,MAAM;IAC1B,YAAY,YAAY,EAAE,CAAC;IAC3B,YAAY,mBAAmB,EAAE,EAAE;IACnC,YAAY,MAAM,EAAE,IAAI;IACxB,YAAY,gBAAgB,EAAE,gBAAgB;IAC9C,YAAY,MAAM,EAAE,mDAAmD;IACvE,SAAS;IACT,IAAI;IACJ;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
import FamilyControls
|
|
3
|
+
import Foundation
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
@objc(ElizaAppBlockerPlugin)
|
|
7
|
+
public class ElizaAppBlockerPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
8
|
+
public let identifier = "ElizaAppBlockerPlugin"
|
|
9
|
+
public let jsName = "ElizaAppBlocker"
|
|
10
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
11
|
+
CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
|
|
12
|
+
CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "getInstalledApps", returnType: CAPPluginReturnPromise),
|
|
14
|
+
CAPPluginMethod(name: "selectApps", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "blockApps", returnType: CAPPluginReturnPromise),
|
|
16
|
+
CAPPluginMethod(name: "unblockApps", returnType: CAPPluginReturnPromise),
|
|
17
|
+
CAPPluginMethod(name: "getStatus", returnType: CAPPluginReturnPromise),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
@objc public override func checkPermissions(_ call: CAPPluginCall) {
|
|
21
|
+
call.resolve(buildPermissionResult())
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@objc public override func requestPermissions(_ call: CAPPluginCall) {
|
|
25
|
+
if #available(iOS 16.0, *) {
|
|
26
|
+
Task { @MainActor in
|
|
27
|
+
var reasonOverride: String?
|
|
28
|
+
do {
|
|
29
|
+
try await AuthorizationCenter.shared.requestAuthorization(for: .individual)
|
|
30
|
+
} catch {
|
|
31
|
+
reasonOverride = error.localizedDescription
|
|
32
|
+
}
|
|
33
|
+
call.resolve(buildPermissionResult(reasonOverride: reasonOverride))
|
|
34
|
+
}
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
AuthorizationCenter.shared.requestAuthorization { result in
|
|
39
|
+
let reasonOverride: String?
|
|
40
|
+
switch result {
|
|
41
|
+
case .success:
|
|
42
|
+
reasonOverride = nil
|
|
43
|
+
case .failure(let error):
|
|
44
|
+
reasonOverride = error.localizedDescription
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
call.resolve(self.buildPermissionResult(reasonOverride: reasonOverride))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@objc func getInstalledApps(_ call: CAPPluginCall) {
|
|
52
|
+
call.resolve([
|
|
53
|
+
"apps": [],
|
|
54
|
+
])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@objc func selectApps(_ call: CAPPluginCall) {
|
|
58
|
+
guard let presenter = bridge?.viewController else {
|
|
59
|
+
call.reject("Could not present the iPhone app picker.")
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Task { @MainActor in
|
|
64
|
+
FamilyActivityPickerBridge.present(from: presenter) { tokens, cancelled in
|
|
65
|
+
let apps = tokens.enumerated().compactMap { index, token -> JSObject? in
|
|
66
|
+
guard let tokenData = AppBlockerShared.serializeToken(token) else {
|
|
67
|
+
return nil
|
|
68
|
+
}
|
|
69
|
+
var object = JSObject()
|
|
70
|
+
object["packageName"] = ""
|
|
71
|
+
object["displayName"] = "Selected App \(index + 1)"
|
|
72
|
+
object["tokenData"] = tokenData
|
|
73
|
+
return object
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
call.resolve([
|
|
77
|
+
"apps": JSArray(apps),
|
|
78
|
+
"cancelled": cancelled,
|
|
79
|
+
])
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@objc func blockApps(_ call: CAPPluginCall) {
|
|
85
|
+
if AuthorizationCenter.shared.authorizationStatus != .approved {
|
|
86
|
+
call.resolve([
|
|
87
|
+
"success": false,
|
|
88
|
+
"endsAt": NSNull(),
|
|
89
|
+
"error": nullable(permissionReason(for: AuthorizationCenter.shared.authorizationStatus)),
|
|
90
|
+
"blockedCount": 0,
|
|
91
|
+
])
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let durationMinutes = call.getInt("durationMinutes")
|
|
96
|
+
if let durationMinutes, durationMinutes > 0 {
|
|
97
|
+
call.resolve([
|
|
98
|
+
"success": false,
|
|
99
|
+
"endsAt": NSNull(),
|
|
100
|
+
"error": "Timed iPhone app blocking still needs a DeviceActivity extension. Start an indefinite block for now and unblock it manually.",
|
|
101
|
+
"blockedCount": 0,
|
|
102
|
+
])
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let tokenDataArray = (call.getArray("appTokens") ?? []).compactMap { $0 as? String }
|
|
107
|
+
let tokens = AppBlockerShared.deserializeTokens(tokenDataArray)
|
|
108
|
+
guard !tokens.isEmpty else {
|
|
109
|
+
call.resolve([
|
|
110
|
+
"success": false,
|
|
111
|
+
"endsAt": NSNull(),
|
|
112
|
+
"error": "Select at least one iPhone app to block.",
|
|
113
|
+
"blockedCount": 0,
|
|
114
|
+
])
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
AppBlockerShared.startBlock(tokens: tokens)
|
|
119
|
+
call.resolve([
|
|
120
|
+
"success": true,
|
|
121
|
+
"endsAt": NSNull(),
|
|
122
|
+
"blockedCount": tokens.count,
|
|
123
|
+
])
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@objc func unblockApps(_ call: CAPPluginCall) {
|
|
127
|
+
AppBlockerShared.stopBlock()
|
|
128
|
+
call.resolve([
|
|
129
|
+
"success": true,
|
|
130
|
+
])
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@objc func getStatus(_ call: CAPPluginCall) {
|
|
134
|
+
let state = AppBlockerShared.loadState()
|
|
135
|
+
let permission = buildPermissionResult()
|
|
136
|
+
let permissionStatus = permission["status"] as? String ?? "not-determined"
|
|
137
|
+
let reason = permission["reason"] as? String
|
|
138
|
+
let blockedCount = state?.tokenDataArray.count ?? 0
|
|
139
|
+
|
|
140
|
+
call.resolve([
|
|
141
|
+
"available": true,
|
|
142
|
+
"active": permissionStatus == "granted" && blockedCount > 0,
|
|
143
|
+
"platform": "ios",
|
|
144
|
+
"engine": "family-controls",
|
|
145
|
+
"blockedCount": blockedCount,
|
|
146
|
+
"blockedPackageNames": [],
|
|
147
|
+
"endsAt": nullable(AppBlockerShared.endsAtString(for: state)),
|
|
148
|
+
"permissionStatus": permissionStatus,
|
|
149
|
+
"reason": nullable(reason),
|
|
150
|
+
])
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private func buildPermissionResult(reasonOverride: String? = nil) -> [String: Any] {
|
|
154
|
+
let status = AuthorizationCenter.shared.authorizationStatus
|
|
155
|
+
let mappedStatus: String
|
|
156
|
+
let canRequest: Bool
|
|
157
|
+
|
|
158
|
+
switch status {
|
|
159
|
+
case .approved:
|
|
160
|
+
mappedStatus = "granted"
|
|
161
|
+
canRequest = false
|
|
162
|
+
case .notDetermined:
|
|
163
|
+
mappedStatus = "not-determined"
|
|
164
|
+
canRequest = true
|
|
165
|
+
default:
|
|
166
|
+
mappedStatus = "denied"
|
|
167
|
+
canRequest = true
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
var result: [String: Any] = [
|
|
171
|
+
"status": mappedStatus,
|
|
172
|
+
"canRequest": canRequest,
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
if let reason = reasonOverride ?? permissionReason(for: status) {
|
|
176
|
+
result["reason"] = reason
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private func permissionReason(for status: AuthorizationStatus) -> String? {
|
|
183
|
+
switch status {
|
|
184
|
+
case .approved:
|
|
185
|
+
return nil
|
|
186
|
+
case .notDetermined:
|
|
187
|
+
return "Authorize Family Controls before Eliza can choose and shield apps on this iPhone."
|
|
188
|
+
default:
|
|
189
|
+
return "Family Controls access is currently denied for this app. Re-run authorization on this iPhone developer build."
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private func nullable(_ value: Any?) -> Any {
|
|
194
|
+
value ?? NSNull()
|
|
195
|
+
}
|
|
196
|
+
}
|