first commit

This commit is contained in:
ytc1012
2025-11-13 15:45:28 +08:00
commit 6b321890c0
54 changed files with 8412 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// TimeTrackingPlugin
let controller = window?.rootViewController as! FlutterViewController
TimeTrackingPlugin.register(with: registrar(forPlugin: "TimeTrackingPlugin")!)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@@ -0,0 +1,154 @@
import Flutter
import UIKit
import ScreenTime
@available(iOS 14.0, *)
public class TimeTrackingPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "autotime_tracker/time_tracking",
binaryMessenger: registrar.messenger()
)
let instance = TimeTrackingPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "hasPermission":
hasPermission(result: result)
case "requestPermission":
requestPermission(result: result)
case "getAppUsage":
getAppUsage(call: call, result: result)
case "startBackgroundTracking":
startBackgroundTracking(result: result)
case "stopBackgroundTracking":
stopBackgroundTracking(result: result)
case "isBackgroundTrackingActive":
isBackgroundTrackingActive(result: result)
default:
result(FlutterMethodNotImplemented)
}
}
// MARK: - Permission Methods
private func hasPermission(result: @escaping FlutterResult) {
if #available(iOS 14.0, *) {
let authorizationStatus = STScreenTime.getAuthorizationStatus()
result(authorizationStatus == .authorized)
} else {
result(false)
}
}
private func requestPermission(result: @escaping FlutterResult) {
if #available(iOS 14.0, *) {
STScreenTime.requestAuthorization { success, error in
if let error = error {
result(FlutterError(
code: "AUTHORIZATION_FAILED",
message: error.localizedDescription,
details: nil
))
} else {
result(success)
}
}
} else {
result(FlutterError(
code: "UNSUPPORTED_VERSION",
message: "Screen Time API requires iOS 14.0 or later",
details: nil
))
}
}
// MARK: - App Usage Methods
@available(iOS 14.0, *)
private func getAppUsage(call: FlutterMethodCall, result: @escaping FlutterResult) {
guard let args = call.arguments as? [String: Any],
let startTimeMs = args["startTime"] as? Int64,
let endTimeMs = args["endTime"] as? Int64 else {
result(FlutterError(
code: "INVALID_ARGUMENTS",
message: "Invalid arguments",
details: nil
))
return
}
let startTime = Date(timeIntervalSince1970: Double(startTimeMs) / 1000.0)
let endTime = Date(timeIntervalSince1970: Double(endTimeMs) / 1000.0)
//
guard STScreenTime.getAuthorizationStatus() == .authorized else {
result(FlutterError(
code: "PERMISSION_DENIED",
message: "Screen Time permission not granted",
details: nil
))
return
}
// 使
STScreenTime.getAppUsage(from: startTime, to: endTime) { usage, error in
if let error = error {
result(FlutterError(
code: "GET_USAGE_FAILED",
message: error.localizedDescription,
details: nil
))
return
}
guard let usage = usage else {
result([])
return
}
// Flutter
var appUsageList: [[String: Any]] = []
// 使
// Screen Time API 使
// API
//
for (bundleId, timeInterval) in usage {
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ?? bundleId
appUsageList.append([
"packageName": bundleId,
"appName": appName,
"startTime": startTime.timeIntervalSince1970 * 1000,
"endTime": endTime.timeIntervalSince1970 * 1000,
"duration": Int(timeInterval),
"deviceUnlockCount": 0
])
}
result(appUsageList)
}
}
// MARK: - Background Tracking Methods
private func startBackgroundTracking(result: @escaping FlutterResult) {
// iOS
// iOS
// 使 Background Tasks Notification Service Extension
result(true)
}
private func stopBackgroundTracking(result: @escaping FlutterResult) {
result(true)
}
private func isBackgroundTrackingActive(result: @escaping FlutterResult) {
result(false)
}
}