Swift использует Семейный контроль, чтобы ограничить приложения и получить название приложенияIOS

Программируем под IOS
Ответить
Anonymous
 Swift использует Семейный контроль, чтобы ограничить приложения и получить название приложения

Сообщение Anonymous »

Я пытаюсь использовать модуль FamilyControls, чтобы ограничить время работы приложений. Ограничитель работает правильно, но я хочу видеть названия приложений, которые ограничены, в моем приложении Flutter. Я перепробовал все, но не могу понять, как получить идентификаторы пакетов этих приложений.
Я знаю, что это возможно, потому что существуют блокировщики приложений, которые это делают.
for (index, token) in applications.applicationTokens.enumerated() {
NSLog("Debug: test 1: \(applications)")
NSLog("Debug: test 2: \(type(of: applications.applicationTokens.enumerated()))")
NSLog("Debug: index: \(index)")
NSLog("Debug: token: \(token)")
}

Этот код дает следующий результат:
Отладка: тест 1: FamilyActivitySelection(includeEntireCategory: false, applicationTokens: Set([ApplicationToken(data: 128) байты: `D^R'įÅyñxnÆøH4sr;Øý³¶ï(DA^_ÒÖÜÄ~
Å^A¨u^O^ÎÕ^WÆG.aBÍEiÿ_n!jÜj¾U^Hi¼ý^[,H2v7cî·(o[Þò¤¤Þ^[ã/^Ní;ÿÙL^P=Ý ^]®+>¹· Á)]), категорияТокены: Set([]), webDomainTokens: Set([]), unkenizedApplicationIdentifiers: Set([]), unkenizedCategoryIdentifiers: Set([]), unkenizedWebDomainIdentifiers: Set([]))
Отладка: тест 2: EnumeratedSequence< Set>
Отладка: индекс: 0
Отладка: токен: ApplicationToken(данные: 128 байт)
Вот часть моего кода (я добавил много, потому что понятия не имею, что происходит):
DeviceActivityMonitorExtension.swift:
typealias ApplicationToken = ManagedSettings.Token
typealias ActivityCategoryToken = ManagedSettings.Token

class DeviceActivityMonitorExtension: DeviceActivityMonitor {
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
}

override func intervalDidEnd(for activity: DeviceActivityName) {
super.intervalDidEnd(for: activity)
}

override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventDidReachThreshold(event, activity: activity)

NSLog("Threshold reached for event: \(event.rawValue)")

// Retrieve stored tokens from shared UserDefaults
guard let defaults = UserDefaults(suiteName: appGroupID) else {
NSLog("No app group defaults available.")
return
}

let decoder = JSONDecoder()
var applicationTokens: [ApplicationToken] = []
var categoryTokens: [ActivityCategoryToken] = []

// Decode application tokens from the new key
if let appTokensData = defaults.data(forKey: Constants.limitedApplicationTokensKey) {
if let tokens = try? decoder.decode([ApplicationToken].self, from: appTokensData) {
applicationTokens = tokens
NSLog("Decoded \(tokens.count) application tokens.")
} else {
NSLog("Failed to decode application tokens.")
}
} else {
NSLog("No application tokens found in UserDefaults.")
}

// Decode category tokens if available
if let catTokensData = defaults.data(forKey: Constants.limitedCategoryIdentifiersKey) {
if let cats = try? decoder.decode([ActivityCategoryToken].self, from: catTokensData) {
categoryTokens = cats
NSLog("Decoded \(cats.count) category tokens.")
} else {
NSLog("Failed to decode category tokens.")
}
} else {
NSLog("No category tokens found in UserDefaults.")
}

// Apply restrictions now that the threshold is reached
let store = ManagedSettingsStore()

// If we have no application tokens, set nil; otherwise, set them as a Set
store.shield.applications = applicationTokens.isEmpty ? nil : Set(applicationTokens)

// For categories, we use a specific policy if we have any
store.shield.applicationCategories = categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.specific(Set(categoryTokens))

NSLog("Restrictions applied. The selected apps and categories should now show the Restricted screen.")
}

override func intervalWillStartWarning(for activity: DeviceActivityName) {
super.intervalWillStartWarning(for: activity)
}

override func intervalWillEndWarning(for activity: DeviceActivityName) {
super.intervalWillEndWarning(for: activity)
}

override func eventWillReachThresholdWarning(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventWillReachThresholdWarning(event, activity: activity)
}
}


Моямодель:
import Foundation
import FamilyControls
import ManagedSettings

private let _MyModel = MyModel()

class MyModel: ObservableObject {
let store = ManagedSettingsStore()

@Published var selectionToDiscourage: FamilyActivitySelection
@Published var selectionToEncourage: FamilyActivitySelection
@Published var selectionToLimit: FamilyActivitySelection

init() {
selectionToDiscourage = FamilyActivitySelection()
selectionToEncourage = FamilyActivitySelection()
selectionToLimit = FamilyActivitySelection()
}

class var shared: MyModel {
return _MyModel
}
func setShieldRestrictions() {
NSLog("Debug: Setting shield restrictions for discouraged apps: \(selectionToDiscourage.applicationTokens)")
let applications = self.selectionToDiscourage
store.shield.applications = applications.applicationTokens.isEmpty ? nil : applications.applicationTokens
store.shield.applicationCategories = applications.categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.specific(applications.categoryTokens)
}

// Used to encode codable to UserDefaults
private let encoder = PropertyListEncoder()

// Used to decode codable from UserDefaults
private let decoder = PropertyListDecoder()

func saveFamilyActivitySelection(selection: FamilyActivitySelection) {
NSLog("Debug: selected app updated: ", selection.applicationTokens.count," category: ", selection.categoryTokens.count)
let defaults = UserDefaults.standard

defaults.set(
try? encoder.encode(selection),
forKey: "limitedApplicationTokens"
)

//check is data saved to user defaults
getSavedFamilyActivitySelection()
}

//get saved family activity selection from UserDefault
func getSavedFamilyActivitySelection() -> FamilyActivitySelection? {
let defaults = UserDefaults.standard
guard let data = defaults.data(forKey: "limitedApplicationTokens") else {
return nil
}
var selectedApp: FamilyActivitySelection?
let decoder = PropertyListDecoder()
selectedApp = try? decoder.decode(FamilyActivitySelection.self, from: data)

NSLog("Debug: saved selected app updated: ", selectedApp?.categoryTokens.count ?? "0")
return selectedApp
}

}

AppDelegate.swift:
func clearLimitedAppsData() {
guard let defaults = UserDefaults(suiteName: Constants.appGroupID) else { return }
defaults.removeObject(forKey: Constants.limitedApplicationBundleIdentifiersKey)
defaults.synchronize()
NSLog("Debug: Cleared limitedApplicationBundleIdentifiersKey data.")
}

var globalMethodCall = ""

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

// clearLimitedAppsData()

guard let controller = window?.rootViewController as? FlutterViewController else {
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

let METHOD_CHANNEL_NAME = "flutter_screentime"

let model = MyModel.shared
let store = ManagedSettingsStore()

let methodChannel = FlutterMethodChannel(name: METHOD_CHANNEL_NAME, binaryMessenger: controller.binaryMessenger)

methodChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in
Task {
// Request authorization for Family Controls if available
if #available(iOS 16.0, *) {
do {
try await AuthorizationCenter.shared.requestAuthorization(for: .individual)
NSLog("Debug: Authorization request succeeded.")
} catch {
NSLog("Debug: Authorization request failed with error: \(error)")
result(FlutterError(code: "AUTH_ERROR", message: "Authorization failed", details: nil))
return
}
} else {
NSLog("Debug: iOS version is below 16.0, cannot proceed.")
result(FlutterError(code: "iOS_VERSION_ERROR", message: "iOS 16.0 or newer is required", details: nil))
return
}

switch call.method {
case "blockApp":
globalMethodCall = "selectAppsToDiscourage"
NSLog("Debug: Presenting blockApp picker.")
let vc = UIHostingController(rootView: ContentView()
.environmentObject(model)
.environmentObject(store))
controller.present(vc, animated: true, completion: nil)
result("blockApp invoked")

case "unblockApp":
globalMethodCall = "selectAppsToEncourage"
NSLog("Debug: Presenting unblockApp picker.")
let vc = UIHostingController(rootView: ContentView()
.environmentObject(model)
.environmentObject(store))
controller.present(vc, animated: true, completion: nil)
result("unblockApp invoked")

case "limitApp":
globalMethodCall = "selectAppsToLimit"
NSLog("Debug: Presenting limitApp picker.")
let vc = UIHostingController(rootView: ContentView()
.environmentObject(model)
.environmentObject(store))
controller.present(vc, animated: true, completion: nil)
result("limitApp invoked")

case "getLimitedApps":
if #available(iOS 16.0, *) {
let limitedApps = self.decodeLimitedApps()
NSLog("Debug: Returning limited apps: \(limitedApps)")
result(limitedApps)
} else {
NSLog("Debug: iOS < 16.0, cannot get limited apps.")
result(["No limited apps or iOS < 16.0"])
}

default:
NSLog("Debug: Unhandled method call: \(call.method)")
result(FlutterMethodNotImplemented)
}
}
}

GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

@available(iOS 16.0, *)
func decodeLimitedApps() -> [String] {
guard let defaults = UserDefaults(suiteName: Constants.appGroupID) else {
NSLog("Debug: No app group defaults available.")
return ["No limited apps found."]
}

let decoder = JSONDecoder()
var limitedApps: [String] = []

NSLog("Debug: Attempting to read limited apps from UserDefaults.")
if let appBundleIDsData = defaults.data(forKey: Constants.limitedApplicationBundleIdentifiersKey) {
NSLog("Debug: Found app bundle IDs data: \(appBundleIDsData.count) bytes")

// Decode the data as [String]
do {
let bundleIDs = try decoder.decode([String].self, from: appBundleIDsData)
if !bundleIDs.isEmpty {
NSLog("Debug: Decoded bundle IDs: \(bundleIDs)")
limitedApps = bundleIDs
for bundleID in bundleIDs {
NSLog("Bundle Identifier: \(bundleID)")
}
} else {
NSLog("Debug: Decoded bundle IDs array is empty.")
}
} catch {
NSLog("Debug: Failed to decode bundle IDs: \(error.localizedDescription)")
// Optionally, log the raw data as a string for inspection
if let rawString = String(data: appBundleIDsData, encoding: .utf8) {
NSLog("Debug: Raw appBundleIDsData as String: \(rawString)")
}
}
} else {
NSLog("Debug: No app bundle IDs data found in UserDefaults.")
}

if limitedApps.isEmpty {
return ["No limited apps found!"]
}

return limitedApps
}

func storeLimitedApps(bundleIDs: [String], categoryIDs: [String]) {
guard let defaults = UserDefaults(suiteName: Constants.appGroupID) else {
NSLog("Debug: Failed to access UserDefaults with App Group ID.")
return
}

let encoder = JSONEncoder()

do {
let appData = try encoder.encode(bundleIDs)
defaults.set(appData, forKey: Constants.limitedApplicationBundleIdentifiersKey)
NSLog("Debug: Stored \(bundleIDs.count) application bundle identifiers: \(bundleIDs)")
} catch {
NSLog("Debug: Failed to encode application bundle identifiers: \(error.localizedDescription)")
}

// Similarly handle categoryIDs if necessary
do {
let catData = try encoder.encode(categoryIDs)
defaults.set(catData, forKey: Constants.limitedCategoryIdentifiersKey)
NSLog("Debug: Stored \(categoryIDs.count) category identifiers.")
} catch {
NSLog("Debug: Failed to encode category identifiers: \(error.localizedDescription)")
}

defaults.synchronize()
}

}



Подробнее здесь: https://stackoverflow.com/questions/792 ... ame-of-app
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»