expo-app-blocker 0.1.46 → 0.1.48
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/README.md +47 -0
- package/android/src/main/java/expo/modules/appblocker/AppBlockerPrefs.kt +62 -2
- package/android/src/main/java/expo/modules/appblocker/ExpoAppBlockerModule.kt +11 -0
- package/android/src/main/java/expo/modules/appblocker/OverlayManager.kt +34 -6
- package/package.json +1 -1
- package/plugin/src/index.js +33 -0
- package/src/ExpoAppBlocker.types.ts +14 -0
package/README.md
CHANGED
|
@@ -235,6 +235,53 @@ No special setup required beyond what the config plugin handles automatically.
|
|
|
235
235
|
| `ios.notification.attachIcon` | `boolean` | `true` | Whether to attach the shield icon as a notification image. Set to `false` to avoid the duplicate-icon look on iOS notification banners (the system app icon is always shown either way). |
|
|
236
236
|
| `android.notificationTitle` | `string` | `"App Blocked"` | Notification title |
|
|
237
237
|
| `android.notificationText` | `string` | `"{appName} is blocked."` | Notification text |
|
|
238
|
+
| `android.overlay.icon` | `string` | — | Path to the brand icon shown above the title in the `SYSTEM_ALERT_WINDOW` overlay. Resolved relative to the project root. PNG with transparent background recommended. Build-time only — not adjustable at runtime. |
|
|
239
|
+
|
|
240
|
+
### Android Overlay (runtime configurable via `setAndroidConfig`)
|
|
241
|
+
|
|
242
|
+
The `SYSTEM_ALERT_WINDOW` overlay flashed on top of a blocked app is fully themeable. All fields below are optional — defaults preserve the previous "App Blocked" + grey-on-white look. Pass them as a single object to `ExpoAppBlocker.setAndroidConfig({ ... })` once at app boot:
|
|
243
|
+
|
|
244
|
+
| Field | Type | Default | Description |
|
|
245
|
+
|---|---|---|---|
|
|
246
|
+
| `overlayTitle` | `string` | `"App Blocked"` | Bold heading. `{appName}` is replaced with the localized app name (e.g. `"Instagram is blocked"`). |
|
|
247
|
+
| `overlayText` | `string` | `"{appName} is blocked."` | Body line under the title. `{appName}` placeholder supported. |
|
|
248
|
+
| `overlayBackgroundColor` | `string` (hex) | `"#FFFFFF"` | Solid background color for the full-screen overlay. |
|
|
249
|
+
| `overlayTitleColor` | `string` (hex) | `"#111111"` | Title text color. |
|
|
250
|
+
| `overlayTextColor` | `string` (hex) | `"#737373"` | Body text color. |
|
|
251
|
+
| `overlayTitleFontSize` | `number` (sp) | `24` | Title font size. Android `sp` units — scales with system font setting. |
|
|
252
|
+
| `overlayTextFontSize` | `number` (sp) | `16` | Body font size. |
|
|
253
|
+
| `overlayTitleBold` | `boolean` | `true` | Render the title with `Typeface.BOLD`. Set to `false` for a regular weight. |
|
|
254
|
+
| `overlayPadding` | `number` (dp) | `32` | Inner padding on all four sides of the overlay's `LinearLayout`. |
|
|
255
|
+
| `overlayIconSize` | `number` (dp) | `96` | Square icon edge length. Only renders when `android.overlay.icon` was declared in the plugin config (build-time). |
|
|
256
|
+
| `overlayIconBottomMargin` | `number` (dp) | `20` | Vertical gap between the icon and the title. |
|
|
257
|
+
| `overlayTitleBottomMargin` | `number` (dp) | `12` | Vertical gap between the title and the body text. |
|
|
258
|
+
| `notificationTitle` | `string` | `"App Blocked"` | Foreground-service notification title. |
|
|
259
|
+
| `notificationText` | `string` | `"{appName} is blocked. Tap to manage."` | Foreground-service notification body. |
|
|
260
|
+
|
|
261
|
+
Example (matches a Hebrew RTL app with brand colors + a logo above the title):
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import * as ExpoAppBlocker from 'expo-app-blocker';
|
|
265
|
+
|
|
266
|
+
ExpoAppBlocker.setAndroidConfig({
|
|
267
|
+
overlayTitle: 'האפליקציה חסומה',
|
|
268
|
+
overlayText: 'ענה על כמה שאלות כדי להשתמש בה',
|
|
269
|
+
overlayBackgroundColor: '#f6f6f6',
|
|
270
|
+
overlayTitleColor: '#111111',
|
|
271
|
+
overlayTextColor: '#888888',
|
|
272
|
+
overlayTitleFontSize: 26,
|
|
273
|
+
overlayTextFontSize: 16,
|
|
274
|
+
overlayTitleBold: true,
|
|
275
|
+
overlayPadding: 32,
|
|
276
|
+
overlayIconSize: 112,
|
|
277
|
+
overlayIconBottomMargin: 20,
|
|
278
|
+
overlayTitleBottomMargin: 12,
|
|
279
|
+
notificationTitle: 'גרנדמייזר',
|
|
280
|
+
notificationText: 'ענה על השאלות כדי לפתוח את האפליקציה',
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Why two layers?** `android.overlay.icon` is build-time because Android resolves drawable resources by ID, which requires the bitmap to be packed into the APK. Everything else (text, colors, sizes) lives in `SharedPreferences` and can be updated by your JS at any time — no rebuild required.
|
|
238
285
|
|
|
239
286
|
### Blur Styles
|
|
240
287
|
|
|
@@ -11,6 +11,13 @@ object AppBlockerPrefs {
|
|
|
11
11
|
private const val KEY_OVERLAY_BG_COLOR = "overlay_bg_color"
|
|
12
12
|
private const val KEY_OVERLAY_TITLE_COLOR = "overlay_title_color"
|
|
13
13
|
private const val KEY_OVERLAY_TEXT_COLOR = "overlay_text_color"
|
|
14
|
+
private const val KEY_OVERLAY_TITLE_FONT_SIZE = "overlay_title_font_size"
|
|
15
|
+
private const val KEY_OVERLAY_TEXT_FONT_SIZE = "overlay_text_font_size"
|
|
16
|
+
private const val KEY_OVERLAY_TITLE_BOLD = "overlay_title_bold"
|
|
17
|
+
private const val KEY_OVERLAY_PADDING = "overlay_padding"
|
|
18
|
+
private const val KEY_OVERLAY_ICON_SIZE = "overlay_icon_size"
|
|
19
|
+
private const val KEY_OVERLAY_ICON_GAP = "overlay_icon_gap"
|
|
20
|
+
private const val KEY_OVERLAY_TITLE_GAP = "overlay_title_gap"
|
|
14
21
|
private const val KEY_NOTIFICATION_TITLE = "notification_title"
|
|
15
22
|
private const val KEY_NOTIFICATION_TEXT = "notification_text"
|
|
16
23
|
|
|
@@ -26,6 +33,13 @@ object AppBlockerPrefs {
|
|
|
26
33
|
.apply()
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Push the overlay + notification config from JS into native prefs.
|
|
38
|
+
*
|
|
39
|
+
* Numeric `Float?` knobs (font sizes, paddings, icon size) are stored as
|
|
40
|
+
* floats and read back through [getOverlayFloat]. Pass `null` to keep the
|
|
41
|
+
* baked-in default; pass an explicit value (e.g. `28f`) to override.
|
|
42
|
+
*/
|
|
29
43
|
fun setAndroidConfig(
|
|
30
44
|
context: Context,
|
|
31
45
|
overlayTitle: String?,
|
|
@@ -33,10 +47,17 @@ object AppBlockerPrefs {
|
|
|
33
47
|
overlayBackgroundColor: String?,
|
|
34
48
|
overlayTitleColor: String?,
|
|
35
49
|
overlayTextColor: String?,
|
|
50
|
+
overlayTitleFontSize: Float?,
|
|
51
|
+
overlayTextFontSize: Float?,
|
|
52
|
+
overlayTitleBold: Boolean?,
|
|
53
|
+
overlayPadding: Float?,
|
|
54
|
+
overlayIconSize: Float?,
|
|
55
|
+
overlayIconBottomMargin: Float?,
|
|
56
|
+
overlayTitleBottomMargin: Float?,
|
|
36
57
|
notificationTitle: String?,
|
|
37
58
|
notificationText: String?,
|
|
38
59
|
) {
|
|
39
|
-
get(context).edit()
|
|
60
|
+
val editor = get(context).edit()
|
|
40
61
|
.putString(KEY_OVERLAY_TITLE, overlayTitle)
|
|
41
62
|
.putString(KEY_OVERLAY_TEXT, overlayText)
|
|
42
63
|
.putString(KEY_OVERLAY_BG_COLOR, overlayBackgroundColor)
|
|
@@ -44,7 +65,18 @@ object AppBlockerPrefs {
|
|
|
44
65
|
.putString(KEY_OVERLAY_TEXT_COLOR, overlayTextColor)
|
|
45
66
|
.putString(KEY_NOTIFICATION_TITLE, notificationTitle)
|
|
46
67
|
.putString(KEY_NOTIFICATION_TEXT, notificationText)
|
|
47
|
-
|
|
68
|
+
putNullableFloat(editor, KEY_OVERLAY_TITLE_FONT_SIZE, overlayTitleFontSize)
|
|
69
|
+
putNullableFloat(editor, KEY_OVERLAY_TEXT_FONT_SIZE, overlayTextFontSize)
|
|
70
|
+
putNullableFloat(editor, KEY_OVERLAY_PADDING, overlayPadding)
|
|
71
|
+
putNullableFloat(editor, KEY_OVERLAY_ICON_SIZE, overlayIconSize)
|
|
72
|
+
putNullableFloat(editor, KEY_OVERLAY_ICON_GAP, overlayIconBottomMargin)
|
|
73
|
+
putNullableFloat(editor, KEY_OVERLAY_TITLE_GAP, overlayTitleBottomMargin)
|
|
74
|
+
if (overlayTitleBold != null) {
|
|
75
|
+
editor.putBoolean(KEY_OVERLAY_TITLE_BOLD, overlayTitleBold)
|
|
76
|
+
} else {
|
|
77
|
+
editor.remove(KEY_OVERLAY_TITLE_BOLD)
|
|
78
|
+
}
|
|
79
|
+
editor.apply()
|
|
48
80
|
}
|
|
49
81
|
|
|
50
82
|
fun getOverlayTitle(context: Context): String =
|
|
@@ -62,9 +94,37 @@ object AppBlockerPrefs {
|
|
|
62
94
|
fun getOverlayTextColor(context: Context): String =
|
|
63
95
|
get(context).getString(KEY_OVERLAY_TEXT_COLOR, null) ?: "#737373"
|
|
64
96
|
|
|
97
|
+
fun getOverlayTitleFontSize(context: Context): Float =
|
|
98
|
+
getOverlayFloat(context, KEY_OVERLAY_TITLE_FONT_SIZE, 24f)
|
|
99
|
+
|
|
100
|
+
fun getOverlayTextFontSize(context: Context): Float =
|
|
101
|
+
getOverlayFloat(context, KEY_OVERLAY_TEXT_FONT_SIZE, 16f)
|
|
102
|
+
|
|
103
|
+
fun getOverlayTitleBold(context: Context): Boolean =
|
|
104
|
+
get(context).getBoolean(KEY_OVERLAY_TITLE_BOLD, true)
|
|
105
|
+
|
|
106
|
+
fun getOverlayPadding(context: Context): Float =
|
|
107
|
+
getOverlayFloat(context, KEY_OVERLAY_PADDING, 32f)
|
|
108
|
+
|
|
109
|
+
fun getOverlayIconSize(context: Context): Float =
|
|
110
|
+
getOverlayFloat(context, KEY_OVERLAY_ICON_SIZE, 96f)
|
|
111
|
+
|
|
112
|
+
fun getOverlayIconBottomMargin(context: Context): Float =
|
|
113
|
+
getOverlayFloat(context, KEY_OVERLAY_ICON_GAP, 20f)
|
|
114
|
+
|
|
115
|
+
fun getOverlayTitleBottomMargin(context: Context): Float =
|
|
116
|
+
getOverlayFloat(context, KEY_OVERLAY_TITLE_GAP, 12f)
|
|
117
|
+
|
|
65
118
|
fun getNotificationTitle(context: Context): String =
|
|
66
119
|
get(context).getString(KEY_NOTIFICATION_TITLE, null) ?: "App Blocked"
|
|
67
120
|
|
|
68
121
|
fun getNotificationText(context: Context): String =
|
|
69
122
|
get(context).getString(KEY_NOTIFICATION_TEXT, null) ?: "{appName} is blocked. Tap to manage."
|
|
123
|
+
|
|
124
|
+
private fun putNullableFloat(editor: SharedPreferences.Editor, key: String, value: Float?) {
|
|
125
|
+
if (value != null) editor.putFloat(key, value) else editor.remove(key)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private fun getOverlayFloat(context: Context, key: String, fallback: Float): Float =
|
|
129
|
+
if (get(context).contains(key)) get(context).getFloat(key, fallback) else fallback
|
|
70
130
|
}
|
|
@@ -66,6 +66,10 @@ class ExpoAppBlockerModule : Module() {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
Function("setAndroidConfig") { config: Map<String, Any?> ->
|
|
69
|
+
// JS sends numbers as Double over the bridge — coerce to Float so prefs
|
|
70
|
+
// keep a consistent type. Booleans are forwarded as-is.
|
|
71
|
+
fun numberOrNull(key: String): Float? = (config[key] as? Number)?.toFloat()
|
|
72
|
+
|
|
69
73
|
AppBlockerPrefs.setAndroidConfig(
|
|
70
74
|
context,
|
|
71
75
|
overlayTitle = config["overlayTitle"] as? String,
|
|
@@ -73,6 +77,13 @@ class ExpoAppBlockerModule : Module() {
|
|
|
73
77
|
overlayBackgroundColor = config["overlayBackgroundColor"] as? String,
|
|
74
78
|
overlayTitleColor = config["overlayTitleColor"] as? String,
|
|
75
79
|
overlayTextColor = config["overlayTextColor"] as? String,
|
|
80
|
+
overlayTitleFontSize = numberOrNull("overlayTitleFontSize"),
|
|
81
|
+
overlayTextFontSize = numberOrNull("overlayTextFontSize"),
|
|
82
|
+
overlayTitleBold = config["overlayTitleBold"] as? Boolean,
|
|
83
|
+
overlayPadding = numberOrNull("overlayPadding"),
|
|
84
|
+
overlayIconSize = numberOrNull("overlayIconSize"),
|
|
85
|
+
overlayIconBottomMargin = numberOrNull("overlayIconBottomMargin"),
|
|
86
|
+
overlayTitleBottomMargin = numberOrNull("overlayTitleBottomMargin"),
|
|
76
87
|
notificationTitle = config["notificationTitle"] as? String,
|
|
77
88
|
notificationText = config["notificationText"] as? String,
|
|
78
89
|
)
|
|
@@ -2,6 +2,7 @@ package expo.modules.appblocker
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.content.Intent
|
|
5
|
+
import android.graphics.BitmapFactory
|
|
5
6
|
import android.graphics.Color
|
|
6
7
|
import android.graphics.PixelFormat
|
|
7
8
|
import android.graphics.Typeface
|
|
@@ -12,6 +13,7 @@ import android.util.TypedValue
|
|
|
12
13
|
import android.view.Gravity
|
|
13
14
|
import android.view.View
|
|
14
15
|
import android.view.WindowManager
|
|
16
|
+
import android.widget.ImageView
|
|
15
17
|
import android.widget.LinearLayout
|
|
16
18
|
import android.widget.TextView
|
|
17
19
|
|
|
@@ -115,7 +117,7 @@ class OverlayManager(private val context: Context) {
|
|
|
115
117
|
|
|
116
118
|
private fun buildOverlayView(appName: String): View {
|
|
117
119
|
val density = context.resources.displayMetrics.density
|
|
118
|
-
fun dp(value:
|
|
120
|
+
fun dp(value: Float) = (value * density).toInt()
|
|
119
121
|
|
|
120
122
|
val overlayTitle = AppBlockerPrefs.getOverlayTitle(context)
|
|
121
123
|
.replace("{appName}", appName)
|
|
@@ -133,26 +135,52 @@ class OverlayManager(private val context: Context) {
|
|
|
133
135
|
AppBlockerPrefs.getOverlayTextColor(context),
|
|
134
136
|
Color.parseColor("#737373"),
|
|
135
137
|
)
|
|
138
|
+
val titleFontSize = AppBlockerPrefs.getOverlayTitleFontSize(context)
|
|
139
|
+
val textFontSize = AppBlockerPrefs.getOverlayTextFontSize(context)
|
|
140
|
+
val titleBold = AppBlockerPrefs.getOverlayTitleBold(context)
|
|
141
|
+
val padding = AppBlockerPrefs.getOverlayPadding(context)
|
|
142
|
+
val iconSize = AppBlockerPrefs.getOverlayIconSize(context)
|
|
143
|
+
val iconGap = AppBlockerPrefs.getOverlayIconBottomMargin(context)
|
|
144
|
+
val titleGap = AppBlockerPrefs.getOverlayTitleBottomMargin(context)
|
|
136
145
|
|
|
137
146
|
return LinearLayout(context).apply {
|
|
138
147
|
orientation = LinearLayout.VERTICAL
|
|
139
148
|
gravity = Gravity.CENTER
|
|
140
149
|
setBackgroundColor(backgroundColor)
|
|
141
|
-
setPadding(dp(
|
|
150
|
+
setPadding(dp(padding), dp(padding), dp(padding), dp(padding))
|
|
151
|
+
|
|
152
|
+
// Optional brand icon — drawable named `expo_app_blocker_overlay_icon`
|
|
153
|
+
// is copied by the config plugin from `pluginConfig.android.overlay.icon`.
|
|
154
|
+
// Skip silently if missing so apps that don't ship one still get a clean overlay.
|
|
155
|
+
val iconResId = context.resources.getIdentifier(
|
|
156
|
+
"expo_app_blocker_overlay_icon",
|
|
157
|
+
"drawable",
|
|
158
|
+
context.packageName,
|
|
159
|
+
)
|
|
160
|
+
if (iconResId != 0) {
|
|
161
|
+
addView(ImageView(context).apply {
|
|
162
|
+
val bitmap = BitmapFactory.decodeResource(context.resources, iconResId)
|
|
163
|
+
if (bitmap != null) setImageBitmap(bitmap)
|
|
164
|
+
val size = dp(iconSize)
|
|
165
|
+
layoutParams = LinearLayout.LayoutParams(size, size).apply {
|
|
166
|
+
bottomMargin = dp(iconGap)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
}
|
|
142
170
|
|
|
143
171
|
addView(TextView(context).apply {
|
|
144
172
|
text = overlayTitle
|
|
145
173
|
setTextColor(titleColor)
|
|
146
|
-
setTextSize(TypedValue.COMPLEX_UNIT_SP,
|
|
147
|
-
setTypeface(typeface, Typeface.BOLD)
|
|
174
|
+
setTextSize(TypedValue.COMPLEX_UNIT_SP, titleFontSize)
|
|
175
|
+
if (titleBold) setTypeface(typeface, Typeface.BOLD)
|
|
148
176
|
gravity = Gravity.CENTER
|
|
149
|
-
setPadding(0, 0, 0, dp(
|
|
177
|
+
setPadding(0, 0, 0, dp(titleGap))
|
|
150
178
|
})
|
|
151
179
|
|
|
152
180
|
addView(TextView(context).apply {
|
|
153
181
|
text = overlayText
|
|
154
182
|
setTextColor(textColor)
|
|
155
|
-
setTextSize(TypedValue.COMPLEX_UNIT_SP,
|
|
183
|
+
setTextSize(TypedValue.COMPLEX_UNIT_SP, textFontSize)
|
|
156
184
|
gravity = Gravity.CENTER
|
|
157
185
|
})
|
|
158
186
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-app-blocker",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "Expo module for cross-platform app blocking. Android: UsageStatsManager + Overlay. iOS: Screen Time API (FamilyControls + ManagedSettings + DeviceActivity).",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
package/plugin/src/index.js
CHANGED
|
@@ -144,6 +144,39 @@ function withAppBlockerAndroid(config, pluginConfig) {
|
|
|
144
144
|
]);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
// Copy the overlay icon (PNG) to `res/drawable/expo_app_blocker_overlay_icon.png`
|
|
148
|
+
// so `OverlayManager.kt` can resolve it via Resources.getIdentifier(...).
|
|
149
|
+
// The icon is rendered above the title in the SYSTEM_ALERT_WINDOW overlay.
|
|
150
|
+
// Path is resolved relative to the project root for consistency with the
|
|
151
|
+
// top-level `icon` config field.
|
|
152
|
+
const overlayIconRel = pluginConfig?.android?.overlay?.icon;
|
|
153
|
+
if (overlayIconRel) {
|
|
154
|
+
config = withDangerousMod(config, [
|
|
155
|
+
"android",
|
|
156
|
+
(config) => {
|
|
157
|
+
const platformRoot = config.modRequest.platformProjectRoot;
|
|
158
|
+
const projectRoot = config.modRequest.projectRoot;
|
|
159
|
+
const drawableDir = path.join(platformRoot, "app", "src", "main", "res", "drawable");
|
|
160
|
+
const iconSrc = path.isAbsolute(overlayIconRel)
|
|
161
|
+
? overlayIconRel
|
|
162
|
+
: path.join(projectRoot, overlayIconRel);
|
|
163
|
+
|
|
164
|
+
if (!fs.existsSync(iconSrc)) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`[expo-app-blocker] android.overlay.icon points to a missing file: ${iconSrc}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!fs.existsSync(drawableDir)) {
|
|
171
|
+
fs.mkdirSync(drawableDir, { recursive: true });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fs.copyFileSync(iconSrc, path.join(drawableDir, "expo_app_blocker_overlay_icon.png"));
|
|
175
|
+
return config;
|
|
176
|
+
},
|
|
177
|
+
]);
|
|
178
|
+
}
|
|
179
|
+
|
|
147
180
|
return config;
|
|
148
181
|
}
|
|
149
182
|
|
|
@@ -159,6 +159,20 @@ export interface AndroidConfig {
|
|
|
159
159
|
overlayTitleColor?: string;
|
|
160
160
|
/** Hex color (e.g. "#737373") for the overlay body text. Default: "#737373". */
|
|
161
161
|
overlayTextColor?: string;
|
|
162
|
+
/** Title font size in sp. Default: 24. */
|
|
163
|
+
overlayTitleFontSize?: number;
|
|
164
|
+
/** Body font size in sp. Default: 16. */
|
|
165
|
+
overlayTextFontSize?: number;
|
|
166
|
+
/** Render the title in bold. Default: true. */
|
|
167
|
+
overlayTitleBold?: boolean;
|
|
168
|
+
/** Inner padding (all sides) in dp. Default: 32. */
|
|
169
|
+
overlayPadding?: number;
|
|
170
|
+
/** Icon edge length in dp (square). Default: 96. Only used when an overlay icon is configured via the plugin. */
|
|
171
|
+
overlayIconSize?: number;
|
|
172
|
+
/** Vertical gap (dp) between the icon and the title. Default: 20. */
|
|
173
|
+
overlayIconBottomMargin?: number;
|
|
174
|
+
/** Vertical gap (dp) between the title and the body text. Default: 12. */
|
|
175
|
+
overlayTitleBottomMargin?: number;
|
|
162
176
|
/** Notification title when app is blocked. Use {appName} as placeholder. Default: "App Blocked" */
|
|
163
177
|
notificationTitle?: string;
|
|
164
178
|
/** Notification text when app is blocked. Use {appName} as placeholder. */
|