Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions packages/react-native-device-activity/ios/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,154 @@ func notifyAppWithName(name: String) {
CFNotificationCenterPostNotification(notificationCenter, notificationName, nil, nil, false)
}

func executeGenericAction(
action: [String: Any],
placeholders: [String: String?],
triggeredBy: String,
applicationToken: ApplicationToken? = nil,
webdomainToken: WebDomainToken? = nil,
categoryToken: ActivityCategoryToken? = nil
) {
let type = action["type"] as? String

if let sleepBefore = action["sleepBefore"] as? Int {
sleep(ms: sleepBefore)
}

if type == "addCurrentToWhitelist" {
var selection = getCurrentWhitelist()

if let applicationToken = applicationToken {
selection.applicationTokens.insert(applicationToken)
}

if let webdomainToken = webdomainToken {
selection.webDomainTokens.insert(webdomainToken)
}

if let categoryToken = categoryToken {
selection.categoryTokens.insert(categoryToken)
}

saveCurrentWhitelist(whitelist: selection)
updateBlock(triggeredBy: "shieldAction")
}

if type == "blockSelection" {
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {
updateShield(
shieldId: action["shieldId"] as? String,
triggeredBy: triggeredBy,
activitySelectionId: familyActivitySelectionId
)

sleep(ms: 50)

blockSelectedApps(
blockSelection: activitySelection,
triggeredBy: triggeredBy
)
} else {
logger.log("No familyActivitySelection found with ID: \(familyActivitySelectionId)")
}
}
} else if type == "unblockSelection" {
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {

unblockSelection(
removeSelection: activitySelection,
triggeredBy: triggeredBy
)

userDefaults?
.removeObject(
forKey: SHIELD_CONFIGURATION_FOR_SELECTION_PREFIX + "_" + familyActivitySelectionId)
}
}
} else if type == "addSelectionToWhitelist" {
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
addSelectionToWhitelistAndUpdateBlock(
whitelistSelection: selection,
triggeredBy: triggeredBy
)
}
} else if type == "removeSelectionFromWhitelist" {
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
removeSelectionFromWhitelistAndUpdateBlock(
selection: selection,
triggeredBy: triggeredBy
)
}
} else if type == "clearWhitelistAndUpdateBlock" {
logger.info("should clearWhitelistAndUpdateBlock")
clearWhitelist()
updateBlock(triggeredBy: triggeredBy)
logger.info("done")
} else if type == "resetBlocks" {
resetBlocks(triggeredBy: triggeredBy)
} else if type == "clearWhitelist" {
clearWhitelist()
} else if type == "disableBlockAllMode" {
disableBlockAllMode(triggeredBy: triggeredBy)
} else if type == "openApp" {
// todo: replace with general string
openUrl(urlString: "device-activity://")

sleep(ms: 1000)
} else if type == "enableBlockAllMode" {
updateShield(
shieldId: action["shieldId"] as? String,
triggeredBy: triggeredBy,
activitySelectionId: nil
)

// sometimes the shield doesn't pick up the shield config change above, trying a sleep to get around it
sleep(ms: 50)

enableBlockAllMode(triggeredBy: triggeredBy)
} else if type == "removeAllPendingNotificationRequests" {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
} else if type == "removeAllDeliveredNotifications" {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
} else if type == "removePendingNotificationRequests" {
if let identifiers = action["identifiers"] as? [String] {
UNUserNotificationCenter
.current()
.removePendingNotificationRequests(withIdentifiers: identifiers)
}
} else if type == "setBadgeCount" {
let actionWithReplacedPlaceholders = replacePlaceholdersInObject(action, with: placeholders)
if let count = actionWithReplacedPlaceholders["count"] as? Int {
if #available(iOS 16.0, *) {
UNUserNotificationCenter
.current()
.setBadgeCount(count)
}
}
} else if type == "sendNotification" {
if let notification = action["payload"] as? [String: Any] {
sendNotification(contents: notification, placeholders: placeholders)
}
} else if type == "sendHttpRequest" {
if let url = action["url"] as? String {
let config = action["options"] as? [String: Any] ?? [:]

task = sendHttpRequest(with: url, config: config, placeholders: placeholders)

// required for it to have time to trigger before process/callback ends
sleep(ms: 1000)
}
}

if let sleepAfter = action["sleepAfter"] as? Int {
sleep(ms: sleepAfter)
}
}

func sendNotification(contents: [String: Any], placeholders: [String: String?]) {
let content = UNMutableNotificationContent()

Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-device-activity/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-device-activity",
"version": "0.4.24",
"version": "0.4.27",
"description": "Provides access to Apples DeviceActivity API",
"main": "build/index.js",
"types": "build/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export type DeviceActivityEvent = {
export type ShieldActionType =
| "disableBlockAllMode"
| "dismiss"
| "whitelistCurrent"
| "addCurrentToWhitelist"
| "unblockPossibleFamilyActivitySelection"
| "unblockAllPossibleFamilyActivitySelections"
| "whitelistPossibleFamilyActivitySelection"
Expand All @@ -170,14 +170,16 @@ export type ShieldActionType =
| "openApp";

export type ShieldAction = {
type: ShieldActionType;
/** @deprecated use actions instead */
type?: ShieldActionType;
delay?: number;
payload?: NotificationPayload;
/**
* defaults to true
*/
onlyFamilySelectionIdsContainingMonitoredActivityNames?: boolean;
behavior: "close" | "defer";
actions?: Action[];
};

export type ShieldActions = {
Expand Down Expand Up @@ -267,6 +269,23 @@ export type Action =
body?: Record<string, any>;
headers?: Record<string, string>;
};
} & CommonTypeParams)
| ({
type: "setBadgeCount";
count: number | string; // string for placeholder
} & CommonTypeParams)
| ({
type: "removeAllPendingNotificationRequests";
} & CommonTypeParams)
| ({
type: "removePendingNotificationRequests";
identifiers: string[];
} & CommonTypeParams)
| ({
type: "addCurrentToWhitelist";
} & CommonTypeParams)
| ({
type: "removeAllDeliveredNotifications";
} & CommonTypeParams);

export type DeviceActivityEventRaw = Omit<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,109 +9,9 @@ import DeviceActivity
import FamilyControls
import Foundation
import ManagedSettings
import NotificationCenter
import os

func executeAction(action: [String: Any], placeholders: [String: String?], eventKey: String) {
let type = action["type"] as? String

if let sleepBefore = action["sleepBefore"] as? Int {
sleep(ms: sleepBefore)
}

if type == "blockSelection" {
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {
updateShield(
shieldId: action["shieldId"] as? String,
triggeredBy: eventKey,
activitySelectionId: familyActivitySelectionId
)

sleep(ms: 50)

blockSelectedApps(
blockSelection: activitySelection,
triggeredBy: eventKey
)
} else {
logger.log("No familyActivitySelection found with ID: \(familyActivitySelectionId)")
}
}
} else if type == "unblockSelection" {
if let familyActivitySelectionId = action["familyActivitySelectionId"] as? String {
if let activitySelection = getFamilyActivitySelectionById(id: familyActivitySelectionId) {

unblockSelection(
removeSelection: activitySelection,
triggeredBy: eventKey
)

userDefaults?
.removeObject(
forKey: SHIELD_CONFIGURATION_FOR_SELECTION_PREFIX + "_" + familyActivitySelectionId)
}
}
} else if type == "addSelectionToWhitelist" {
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
addSelectionToWhitelistAndUpdateBlock(
whitelistSelection: selection,
triggeredBy: eventKey
)
}
} else if type == "removeSelectionFromWhitelist" {
if let familyActivitySelectionInput = action["familyActivitySelection"] as? [String: Any] {
let selection = parseActivitySelectionInput(input: familyActivitySelectionInput)
removeSelectionFromWhitelistAndUpdateBlock(
selection: selection,
triggeredBy: eventKey
)
}
} else if type == "clearWhitelistAndUpdateBlock" {
clearWhitelist()
updateBlock(triggeredBy: eventKey)
} else if type == "resetBlocks" {
resetBlocks(triggeredBy: eventKey)
} else if type == "clearWhitelist" {
clearWhitelist()
} else if type == "disableBlockAllMode" {
disableBlockAllMode(triggeredBy: eventKey)
} else if type == "openApp" {
// todo: replace with general string
openUrl(urlString: "device-activity://")

sleep(ms: 1000)
} else if type == "enableBlockAllMode" {
updateShield(
shieldId: action["shieldId"] as? String,
triggeredBy: eventKey,
activitySelectionId: nil
)

// sometimes the shield doesn't pick up the shield config change above, trying a sleep to get around it
sleep(ms: 50)

enableBlockAllMode(triggeredBy: eventKey)
} else if type == "sendNotification" {
if let notification = action["payload"] as? [String: Any] {
sendNotification(contents: notification, placeholders: placeholders)
}
} else if type == "sendHttpRequest" {
if let url = action["url"] as? String {
let config = action["options"] as? [String: Any] ?? [:]

task = sendHttpRequest(with: url, config: config, placeholders: placeholders)

// required for it to have time to trigger before process/callback ends
sleep(ms: 1000)
}
}

if let sleepAfter = action["sleepAfter"] as? Int {
sleep(ms: sleepAfter)
}
}

class DeviceActivityMonitorExtension: DeviceActivityMonitor {
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
Expand Down Expand Up @@ -154,7 +54,7 @@ class DeviceActivityMonitorExtension: DeviceActivityMonitor {
callbackName: String,
eventName: String?
) {
let key =
let triggeredBy =
eventName != nil
? "actions_for_\(activityName)_\(callbackName)_\(eventName!)"
: "actions_for_\(activityName)_\(callbackName)"
Expand All @@ -167,7 +67,7 @@ class DeviceActivityMonitorExtension: DeviceActivityMonitor {

CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)

if let actions = userDefaults?.array(forKey: key) {
if let actions = userDefaults?.array(forKey: triggeredBy) {
actions.forEach { actionRaw in
if let action = actionRaw as? [String: Any] {
let skipIfAlreadyTriggeredAfter = action["skipIfAlreadyTriggeredAfter"] as? Double
Expand Down Expand Up @@ -200,10 +100,10 @@ class DeviceActivityMonitorExtension: DeviceActivityMonitor {
callbackName: callbackName,
eventName: eventName
) {
executeAction(
executeGenericAction(
action: action,
placeholders: placeholders,
eventKey: key
triggeredBy: triggeredBy
)
}
}
Expand Down
Loading