forked from firka/student-legacy
- data backup
- I can't test it because I'm stuck on the login screen.
This commit is contained in:
parent
b6fbfd5756
commit
b7b3a37b52
20
refilc/ios/PrivacyInfo.xcprivacy
Normal file
20
refilc/ios/PrivacyInfo.xcprivacy
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<!-- [1] background_fetch: UserDefaults -->
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
@ -17,7 +17,13 @@
|
||||
3127F7A828EAEE8500C2EFB3 /* lesson_model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3127F7A728EAEE8500C2EFB3 /* lesson_model.swift */; };
|
||||
373A6ECB5FC71FE9D8AF2EDB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F0ADD56276103500A3016C8 /* Pods_Runner.framework */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
4F35BF332BE2FFA30098EF72 /* public_vars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F35BF322BE2FFA30098EF72 /* public_vars.swift */; };
|
||||
4F35BF352BE2FFD80098EF72 /* LiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F35BF342BE2FFD80098EF72 /* LiveActivityManager.swift */; };
|
||||
4F35BF362BE2FFD80098EF72 /* LiveActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F35BF342BE2FFD80098EF72 /* LiveActivityManager.swift */; };
|
||||
4F35BF382BE300A70098EF72 /* lesson_model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3127F7A728EAEE8500C2EFB3 /* lesson_model.swift */; };
|
||||
4F35BF392BE300B10098EF72 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
4F35BF3A2BE301180098EF72 /* public_vars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F35BF322BE2FFA30098EF72 /* public_vars.swift */; };
|
||||
4F35BF3E2BE304550098EF72 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 4F35BF3D2BE304550098EF72 /* PrivacyInfo.xcprivacy */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
@ -73,6 +79,10 @@
|
||||
3127F7A728EAEE8500C2EFB3 /* lesson_model.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = lesson_model.swift; sourceTree = "<group>"; };
|
||||
317DE77A294F6FFB002E323E /* livecard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = livecard.entitlements; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
4F35BF322BE2FFA30098EF72 /* public_vars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = public_vars.swift; sourceTree = "<group>"; };
|
||||
4F35BF342BE2FFD80098EF72 /* LiveActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityManager.swift; sourceTree = "<group>"; };
|
||||
4F35BF3B2BE303A40098EF72 /* app_group_directory.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = app_group_directory.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4F35BF3D2BE304550098EF72 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
707F8089D970F81C480F73C4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
@ -118,6 +128,7 @@
|
||||
3127F79728EAEDE300C2EFB3 /* Assets.xcassets */,
|
||||
3127F79928EAEDE300C2EFB3 /* Info.plist */,
|
||||
3127F7A528EAEE5900C2EFB3 /* livecard.swift */,
|
||||
4F35BF342BE2FFD80098EF72 /* LiveActivityManager.swift */,
|
||||
);
|
||||
path = livecard;
|
||||
sourceTree = "<group>";
|
||||
@ -125,6 +136,7 @@
|
||||
6640A963014A9D4F31026053 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4F35BF3B2BE303A40098EF72 /* app_group_directory.framework */,
|
||||
1F0ADD56276103500A3016C8 /* Pods_Runner.framework */,
|
||||
3127F73F28EAEC8A00C2EFB3 /* IntentsUI.framework */,
|
||||
3127F75528EAECC800C2EFB3 /* WidgetKit.framework */,
|
||||
@ -157,6 +169,7 @@
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4F35BF3D2BE304550098EF72 /* PrivacyInfo.xcprivacy */,
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
3127F78F28EAEDE200C2EFB3 /* livecard */,
|
||||
@ -187,6 +200,7 @@
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
4F35BF322BE2FFA30098EF72 /* public_vars.swift */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
@ -242,7 +256,7 @@
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1410;
|
||||
LastSwiftUpdateCheck = 1530;
|
||||
LastUpgradeCheck = 1510;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
@ -290,6 +304,7 @@
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
4F35BF3E2BE304550098EF72 /* PrivacyInfo.xcprivacy in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -320,6 +335,8 @@
|
||||
buildActionMask = 12;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||
);
|
||||
@ -372,7 +389,7 @@
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
buildActionMask = 12;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
@ -391,9 +408,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4F35BF362BE2FFD80098EF72 /* LiveActivityManager.swift in Sources */,
|
||||
3127F7A828EAEE8500C2EFB3 /* lesson_model.swift in Sources */,
|
||||
3127F7A428EAEE3D00C2EFB3 /* livecard.intentdefinition in Sources */,
|
||||
3127F7A628EAEE5900C2EFB3 /* livecard.swift in Sources */,
|
||||
4F35BF3A2BE301180098EF72 /* public_vars.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -401,8 +420,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
4F35BF392BE300B10098EF72 /* AppDelegate.swift in Sources */,
|
||||
4F35BF382BE300A70098EF72 /* lesson_model.swift in Sources */,
|
||||
4F35BF352BE2FFD80098EF72 /* LiveActivityManager.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
4F35BF332BE2FFA30098EF72 /* public_vars.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -496,7 +518,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 250;
|
||||
DEVELOPMENT_TEAM = 4DKAF249F3;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
||||
@ -506,7 +528,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -528,14 +550,14 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 250;
|
||||
DEVELOPMENT_TEAM = 4DKAF249F3;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = livecard/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = livecard;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSSupportsLiveActivities = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -544,7 +566,7 @@
|
||||
MARKETING_VERSION = 5.0.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo.livecardpro;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -570,14 +592,14 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 250;
|
||||
DEVELOPMENT_TEAM = 4DKAF249F3;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = livecard/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = livecard;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSSupportsLiveActivities = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -585,7 +607,7 @@
|
||||
);
|
||||
MARKETING_VERSION = 5.0.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo.livecardpro;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -610,14 +632,14 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 250;
|
||||
DEVELOPMENT_TEAM = 4DKAF249F3;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = livecard/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = livecard;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSSupportsLiveActivities = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -625,7 +647,7 @@
|
||||
);
|
||||
MARKETING_VERSION = 5.0.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo.livecardpro;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo.livecardpro;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -754,7 +776,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 250;
|
||||
DEVELOPMENT_TEAM = 4DKAF249F3;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
||||
@ -764,7 +786,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
@ -782,7 +804,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 250;
|
||||
DEVELOPMENT_TEAM = 4DKAF249F3;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = reFilc;
|
||||
@ -792,7 +814,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.0.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilc2.naplo;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.refilcrel.naplo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -1,25 +1,113 @@
|
||||
import UIKit
|
||||
import background_fetch
|
||||
import ActivityKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
private var methodChannel: FlutterMethodChannel?
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
|
||||
// here, Without this code the task will not work.
|
||||
//SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback(registerPlugins)
|
||||
if #available(iOS 10.0, *) {
|
||||
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
|
||||
guard let controller = window?.rootViewController as? FlutterViewController else {
|
||||
fatalError("rootViewController is not type FlutterViewController")
|
||||
}
|
||||
|
||||
methodChannel = FlutterMethodChannel(name: "hu.refilc/liveactivity",
|
||||
binaryMessenger: controller as! FlutterBinaryMessenger)
|
||||
methodChannel?.setMethodCallHandler({
|
||||
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
|
||||
guard call.method == "createLiveActivity" || call.method == "endLiveActivity" || call.method == "updateLiveActivity" else {
|
||||
result(FlutterMethodNotImplemented)
|
||||
return
|
||||
}
|
||||
self?.handleMethodCall(call, result: result)
|
||||
})
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
|
||||
// here
|
||||
func registerPlugins(registry: FlutterPluginRegistry) {
|
||||
GeneratedPluginRegistrant.register(with: registry)
|
||||
override func applicationWillTerminate(_ application: UIApplication) {
|
||||
if #available(iOS 16.2, *) {
|
||||
LiveActivityManager.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private func handleMethodCall(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
if call.method == "createLiveActivity" {
|
||||
if let args = call.arguments as? [String: Any] {
|
||||
lessonDataDictionary = args
|
||||
globalLessonData = LessonData(from: lessonDataDictionary)
|
||||
print("swift: megkapott flutter adatok:",lessonDataDictionary)
|
||||
print("Live Activity bekapcsolva az eszközön: ",checkLiveActivityFeatureAvailable())
|
||||
if(checkLiveActivityFeatureAvailable()) {
|
||||
createLiveActivity(with: lessonDataDictionary)
|
||||
result(checkLiveActivityFeatureAvailable())
|
||||
} else {
|
||||
result(nil)
|
||||
}
|
||||
|
||||
} else {
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Invalid iOS arguments received", details: nil))
|
||||
}
|
||||
} else if call.method == "updateLiveActivity" {
|
||||
if let args = call.arguments as? [String: Any] {
|
||||
lessonDataDictionary = args
|
||||
globalLessonData = LessonData(from: lessonDataDictionary)
|
||||
updateLiveActivity(with: lessonDataDictionary)
|
||||
result(nil)
|
||||
} else {
|
||||
result(FlutterError(code: "INVALID_ARGUMENTS", message: "Invalid iOS arguments received", details: nil))
|
||||
}
|
||||
} else if call.method == "endLiveActivity" {
|
||||
endLiveActivity()
|
||||
result(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func createLiveActivity(with activityData: [String: Any]) -> String? {
|
||||
var lessonData = LessonData(from: activityData)
|
||||
print("Live Activity létrehozása...")
|
||||
if #available(iOS 16.2, *) {
|
||||
LiveActivityManager.create()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func updateLiveActivity(with activityData: [String: Any]) {
|
||||
let lessonData = LessonData(from: activityData)
|
||||
print("swift: megkapott flutter adatok:",lessonDataDictionary)
|
||||
print("Live Activity frissítés...")
|
||||
if #available(iOS 16.2, *) {
|
||||
LiveActivityManager.update()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func endLiveActivity() {
|
||||
print("Live Activity befejezése...")
|
||||
if #available(iOS 16.2, *) {
|
||||
LiveActivityManager.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private func checkIfLiveActivityExists() -> Bool {
|
||||
if let activityID = activityID {
|
||||
if #available(iOS 16.2, *) {
|
||||
return LiveActivityManager.isRunning(activityID)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func checkLiveActivityFeatureAvailable() -> Bool {
|
||||
if #available(iOS 16.2, *) {
|
||||
guard ActivityAuthorizationInfo().areActivitiesEnabled else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>com.transistorsoft.fetch</string>
|
||||
<string>com.transistorsoft.refilcnotification</string>
|
||||
<string>com.transistorsoft.refilcliveactivity</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
@ -133,5 +134,5 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
12
refilc/ios/Runner/public_vars.swift
Normal file
12
refilc/ios/Runner/public_vars.swift
Normal file
@ -0,0 +1,12 @@
|
||||
//
|
||||
// public_vars.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by Geryy on 02/05/2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
var lessonDataDictionary: [String: Any] = [:]
|
||||
var globalLessonData = LessonData(from: lessonDataDictionary)
|
||||
var activityID: String? = ""
|
92
refilc/ios/livecard/LiveActivityManager.swift
Normal file
92
refilc/ios/livecard/LiveActivityManager.swift
Normal file
@ -0,0 +1,92 @@
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import Foundation
|
||||
|
||||
public struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable {
|
||||
public typealias LiveDeliveryData = ContentState
|
||||
public struct ContentState: Codable, Hashable {
|
||||
var color: String
|
||||
var icon: String
|
||||
var index: String
|
||||
var title: String
|
||||
var subtitle: String
|
||||
var description: String
|
||||
var startDate: Date
|
||||
var endDate: Date
|
||||
var date: ClosedRange<Date>
|
||||
var nextSubject: String
|
||||
var nextRoom: String
|
||||
}
|
||||
|
||||
public var id = UUID()
|
||||
}
|
||||
|
||||
@available(iOS 16.2, *)
|
||||
final class LiveActivityManager {
|
||||
static let shared = LiveActivityManager()
|
||||
var currentActivity: Activity<LiveActivitiesAppAttributes>?
|
||||
|
||||
class func create() {
|
||||
|
||||
Task {
|
||||
do {
|
||||
let contentState = LiveActivitiesAppAttributes.ContentState(color: globalLessonData.color, icon: globalLessonData.icon, index: globalLessonData.index, title: globalLessonData.title, subtitle: globalLessonData.subtitle, description: globalLessonData.description, startDate: globalLessonData.startDate, endDate: globalLessonData.endDate, date: globalLessonData.date, nextSubject: globalLessonData.nextSubject, nextRoom: globalLessonData.nextRoom)
|
||||
|
||||
let activityContent = ActivityContent(state: contentState, staleDate: globalLessonData.endDate, relevanceScore: 0)
|
||||
|
||||
let activity = try Activity<LiveActivitiesAppAttributes>.request(
|
||||
attributes: LiveActivitiesAppAttributes(),
|
||||
content: activityContent,
|
||||
pushType: nil
|
||||
)
|
||||
|
||||
activityID = activity.id
|
||||
print("Live Activity létrehozva. Azonosító: \(activity.id)")
|
||||
} catch {
|
||||
print("Hiba történt a Live Activity létrehozásakor: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class func update() {
|
||||
Task {
|
||||
for activity in Activity<LiveActivitiesAppAttributes>.activities {
|
||||
do {
|
||||
let contentState = LiveActivitiesAppAttributes.ContentState(color: globalLessonData.color, icon: globalLessonData.icon, index: globalLessonData.index, title: globalLessonData.title, subtitle: globalLessonData.subtitle, description: globalLessonData.description, startDate: globalLessonData.startDate, endDate: globalLessonData.endDate, date: globalLessonData.date, nextSubject: globalLessonData.nextSubject, nextRoom: globalLessonData.nextRoom)
|
||||
|
||||
let activityContent = ActivityContent(state: contentState, staleDate: globalLessonData.endDate, relevanceScore: 0)
|
||||
|
||||
await activity.update(activityContent)
|
||||
activityID = activity.id
|
||||
print("Live Activity frissítve. Azonosító: \(activity.id)")
|
||||
} catch {
|
||||
print("Hiba történt a Live Activity frissítésekor: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class func stop() {
|
||||
if (activityID != "") {
|
||||
Task {
|
||||
for activity in Activity<LiveActivitiesAppAttributes>.activities{
|
||||
let contentState = LiveActivitiesAppAttributes.ContentState(color: globalLessonData.color, icon: globalLessonData.icon, index: globalLessonData.index, title: globalLessonData.title, subtitle: globalLessonData.subtitle, description: globalLessonData.description, startDate: globalLessonData.startDate, endDate: globalLessonData.endDate, date: globalLessonData.date, nextSubject: globalLessonData.nextSubject, nextRoom: globalLessonData.nextRoom)
|
||||
|
||||
await activity.end(ActivityContent(state: contentState, staleDate: Date.distantFuture),dismissalPolicy: .immediate)
|
||||
}
|
||||
activityID = nil
|
||||
print("Live Activity sikeresen leállítva")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class func isRunning(_ activityID: String) -> Bool {
|
||||
for activity in Activity<LiveActivitiesAppAttributes>.activities {
|
||||
if activity.id == activityID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import ActivityKit
|
||||
|
||||
class LessonData {
|
||||
public struct LessonData {
|
||||
var color: String
|
||||
var icon: String
|
||||
var index: String
|
||||
@ -13,19 +14,27 @@ class LessonData {
|
||||
var nextSubject: String
|
||||
var nextRoom: String
|
||||
|
||||
init?() {
|
||||
let sharedDefault = UserDefaults(suiteName: "group.refilc2.livecard")!
|
||||
init(from dictionary: [String: Any]) {
|
||||
self.color = dictionary["color"] as? String ?? ""
|
||||
self.icon = dictionary["icon"] as? String ?? ""
|
||||
self.index = dictionary["index"] as? String ?? ""
|
||||
self.title = dictionary["title"] as? String ?? ""
|
||||
self.subtitle = dictionary["subtitle"] as? String ?? ""
|
||||
self.description = dictionary["description"] as? String ?? ""
|
||||
self.nextSubject = dictionary["nextSubject"] as? String ?? ""
|
||||
self.nextRoom = dictionary["nextRoom"] as? String ?? ""
|
||||
|
||||
self.color = sharedDefault.string(forKey: "color")!
|
||||
self.icon = sharedDefault.string(forKey: "icon")!
|
||||
self.index = sharedDefault.string(forKey: "index")!
|
||||
self.title = sharedDefault.string(forKey: "title")!
|
||||
self.subtitle = sharedDefault.string(forKey: "subtitle")!
|
||||
self.description = sharedDefault.string(forKey: "description")!
|
||||
self.startDate = Date(timeIntervalSince1970: Double(sharedDefault.string(forKey: "startDate")!)! / 1000)
|
||||
self.endDate = Date(timeIntervalSince1970: Double(sharedDefault.string(forKey: "endDate")!)! / 1000)
|
||||
if let startDateStr = dictionary["startDate"] as? String, let startDateInt = Int(startDateStr) {
|
||||
self.startDate = Date(timeIntervalSince1970: TimeInterval(startDateInt) / 1000)
|
||||
} else {
|
||||
self.startDate = Date()
|
||||
}
|
||||
|
||||
if let endDateStr = dictionary["endDate"] as? String, let endDateInt = Int(endDateStr) {
|
||||
self.endDate = Date(timeIntervalSince1970: TimeInterval(endDateInt) / 1000)
|
||||
} else {
|
||||
self.endDate = Date()
|
||||
}
|
||||
date = self.startDate...self.endDate
|
||||
self.nextSubject = sharedDefault.string(forKey: "nextSubject")!
|
||||
self.nextRoom = sharedDefault.string(forKey: "nextRoom")!
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import SwiftUI
|
||||
@main
|
||||
struct Widgets: WidgetBundle {
|
||||
var body: some Widget {
|
||||
if #available(iOS 16.1, *) {
|
||||
if #available(iOS 16.2, *) {
|
||||
LiveCardWidget()
|
||||
}
|
||||
}
|
||||
@ -37,22 +37,11 @@ extension Color {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We need to redefined live activities pipe
|
||||
struct LiveActivitiesAppAttributes: ActivityAttributes, Identifiable {
|
||||
public struct ContentState: Codable, Hashable { }
|
||||
|
||||
var id = UUID()
|
||||
}
|
||||
|
||||
struct LockScreenLiveActivityView: View {
|
||||
let context: ActivityViewContext<LiveActivitiesAppAttributes>
|
||||
|
||||
let lesson = LessonData()
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: lesson!.icon)
|
||||
Image(systemName: context.state.icon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: CGFloat(30), height: CGFloat(30))
|
||||
@ -60,17 +49,17 @@ struct LockScreenLiveActivityView: View {
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .center) {
|
||||
Text(lesson!.index + lesson!.title)
|
||||
Text(context.state.index + context.state.title)
|
||||
.font(.title3)
|
||||
.bold()
|
||||
|
||||
Text(lesson!.subtitle)
|
||||
Text(context.state.subtitle)
|
||||
.font(.subheadline)
|
||||
.padding(.trailing, 12)
|
||||
}
|
||||
|
||||
if (lesson!.description != "") {
|
||||
Text(lesson!.description)
|
||||
if (context.state.description != "") {
|
||||
Text(context.state.description)
|
||||
.font(.subheadline)
|
||||
}
|
||||
|
||||
@ -79,16 +68,16 @@ struct LockScreenLiveActivityView: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: CGFloat(8), height: CGFloat(8))
|
||||
Text(lesson!.nextSubject)
|
||||
Text(context.state.nextSubject)
|
||||
.font(.caption)
|
||||
Text(lesson!.nextRoom)
|
||||
Text(context.state.nextRoom)
|
||||
.font(.caption2)
|
||||
}
|
||||
}.padding(15)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(timerInterval: lesson!.date, countsDown: true)
|
||||
Text(timerInterval: context.state.date, countsDown: true)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(width: 85)
|
||||
.font(.title2)
|
||||
@ -96,15 +85,15 @@ struct LockScreenLiveActivityView: View {
|
||||
.padding(.trailing, CGFloat(24))
|
||||
}
|
||||
.activityBackgroundTint(
|
||||
lesson!.color != "#676767"
|
||||
? Color(hex: lesson!.color)
|
||||
context.state.color != "#676767"
|
||||
? Color(hex: context.state.color)
|
||||
// Ha nem megy hat nem megy
|
||||
: Color.clear
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 16.1, *)
|
||||
@available(iOSApplicationExtension 16.2, *)
|
||||
struct LiveCardWidget: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
/// Live Activity Notification
|
||||
@ -112,7 +101,6 @@ struct LiveCardWidget: Widget {
|
||||
LockScreenLiveActivityView(context: context)
|
||||
/// Dynamic Island
|
||||
} dynamicIsland: { context in
|
||||
let lesson = LessonData()
|
||||
|
||||
/// Expanded
|
||||
return DynamicIsland {
|
||||
@ -120,16 +108,16 @@ struct LiveCardWidget: Widget {
|
||||
VStack {
|
||||
Spacer()
|
||||
ProgressView(
|
||||
timerInterval: lesson!.date,
|
||||
timerInterval: context.state.date,
|
||||
countsDown: true,
|
||||
label: {
|
||||
Image(systemName: lesson!.icon)
|
||||
Image(systemName: context.state.icon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: CGFloat(32), height: CGFloat(32))
|
||||
},
|
||||
currentValueLabel: {
|
||||
Image(systemName: lesson!.icon)
|
||||
Image(systemName: context.state.icon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: CGFloat(32), height: CGFloat(32))
|
||||
@ -138,38 +126,34 @@ struct LiveCardWidget: Widget {
|
||||
}
|
||||
}
|
||||
DynamicIslandExpandedRegion(.center) {
|
||||
VStack(alignment: .leading) {
|
||||
Text(lesson!.index + lesson!.title)
|
||||
VStack(alignment: .center) {
|
||||
Text(context.state.index + context.state.title)
|
||||
.lineLimit(1)
|
||||
.font(.title3)
|
||||
.font(.body)
|
||||
.bold()
|
||||
|
||||
Text(lesson!.description)
|
||||
.lineLimit(2)
|
||||
.font(.caption)
|
||||
}.padding(EdgeInsets(top: 0.0, leading: 5.0, bottom: 0.0, trailing: 0.0))
|
||||
}
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text(lesson!.subtitle)
|
||||
Text(context.state.subtitle)
|
||||
.lineLimit(1)
|
||||
.font(.subheadline)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
Text(context.state.description)
|
||||
.lineLimit(2)
|
||||
.font(.caption)
|
||||
}.padding(EdgeInsets(top: 0.0, leading: 5.0, bottom: 0.0, trailing: 0.0))
|
||||
}
|
||||
|
||||
/// Compact
|
||||
} compactLeading: {
|
||||
Label {
|
||||
Text(lesson!.title)
|
||||
Text(context.state.title)
|
||||
} icon: {
|
||||
Image(systemName: lesson!.icon)
|
||||
Image(systemName: context.state.icon)
|
||||
}
|
||||
.font(.caption2)
|
||||
}
|
||||
compactTrailing: {
|
||||
Text(timerInterval: lesson!.date, countsDown: true)
|
||||
Text(timerInterval: context.state.date, countsDown: true)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(width: 40)
|
||||
.font(.caption2)
|
||||
@ -178,16 +162,16 @@ struct LiveCardWidget: Widget {
|
||||
} minimal: {
|
||||
VStack(alignment: .center, content: {
|
||||
ProgressView(
|
||||
timerInterval: lesson!.date,
|
||||
timerInterval: context.state.date,
|
||||
countsDown: true,
|
||||
label: {
|
||||
Image(systemName: lesson!.icon)
|
||||
Image(systemName: context.state.icon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: CGFloat(12), height: CGFloat(12))
|
||||
},
|
||||
currentValueLabel: {
|
||||
Image(systemName: lesson!.icon)
|
||||
Image(systemName: context.state.icon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: CGFloat(12), height: CGFloat(12))
|
||||
@ -196,8 +180,8 @@ struct LiveCardWidget: Widget {
|
||||
})
|
||||
}
|
||||
.keylineTint(
|
||||
lesson!.color != "#676767"
|
||||
? Color(hex: lesson!.color)
|
||||
context.state.color != "#676767"
|
||||
? Color(hex: context.state.color)
|
||||
: Color.clear
|
||||
)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:refilc/api/providers/liveactivity/platform_channel.dart';
|
||||
import 'package:refilc/helpers/subject.dart';
|
||||
import 'package:refilc/models/settings.dart';
|
||||
import 'package:refilc_kreta_api/models/lesson.dart';
|
||||
@ -10,7 +11,6 @@ import 'package:refilc_kreta_api/models/week.dart';
|
||||
import 'package:refilc/utils/format.dart';
|
||||
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:live_activities/live_activities.dart';
|
||||
import 'package:refilc_mobile_ui/pages/home/live_card/live_card.i18n.dart';
|
||||
|
||||
enum LiveCardState {
|
||||
@ -29,6 +29,15 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
Lesson? prevLesson;
|
||||
List<Lesson>? nextLessons;
|
||||
|
||||
// new variables
|
||||
static bool hasActivityStarted = false;
|
||||
static bool hasDayEnd = false;
|
||||
static DateTime? storeFirstRunDate;
|
||||
static bool hasActivitySettingsChanged = false;
|
||||
static Map<String, String> LAData = {};
|
||||
static DateTime? now;
|
||||
//
|
||||
|
||||
LiveCardState currentState = LiveCardState.empty;
|
||||
late Timer _timer;
|
||||
late final TimetableProvider _timetable;
|
||||
@ -36,9 +45,6 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
|
||||
late Duration _delay;
|
||||
|
||||
final _liveActivitiesPlugin = LiveActivities();
|
||||
String? _latestActivityId;
|
||||
Map<String, String> _lastActivity = {};
|
||||
|
||||
bool _hasCheckedTimetable = false;
|
||||
|
||||
@ -47,23 +53,6 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
required SettingsProvider settings,
|
||||
}) : _timetable = timetable,
|
||||
_settings = settings {
|
||||
if (Platform.isIOS) {
|
||||
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
|
||||
// Console log
|
||||
if (kDebugMode) {
|
||||
print("iOS LiveActivity enabled: $value");
|
||||
}
|
||||
|
||||
if (value) {
|
||||
_liveActivitiesPlugin.init(appGroupId: "group.refilc2.livecard");
|
||||
|
||||
_liveActivitiesPlugin.getAllActivitiesIds().then((value) {
|
||||
_latestActivityId = value.isNotEmpty ? value.first : null;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) => update());
|
||||
_delay = settings.bellDelayEnabled
|
||||
? Duration(seconds: settings.bellDelay)
|
||||
@ -71,21 +60,6 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
update();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer.cancel();
|
||||
if (Platform.isIOS) {
|
||||
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
|
||||
if (value) {
|
||||
if (_latestActivityId != null) {
|
||||
_liveActivitiesPlugin.endActivity(_latestActivityId!);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// Debugging
|
||||
static DateTime _now() {
|
||||
// return DateTime(2023, 9, 27, 9, 30);
|
||||
@ -110,6 +84,66 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
|
||||
Map<String, String> toMap() {
|
||||
switch (currentState) {
|
||||
case LiveCardState.morning:
|
||||
return {
|
||||
"color":
|
||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
||||
"icon": nextLesson != null
|
||||
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
||||
: "book",
|
||||
"title": "Első órádig:",
|
||||
"subtitle": "",
|
||||
"description": "",
|
||||
"startDate": storeFirstRunDate != null ? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) - (_delay.inMilliseconds)).toString(): "",
|
||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||
_delay.inMilliseconds)
|
||||
.toString(),
|
||||
"nextSubject": nextLesson != null
|
||||
? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||
: "",
|
||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||
};
|
||||
|
||||
case LiveCardState.afternoon:
|
||||
return {
|
||||
"color":
|
||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
||||
"icon": nextLesson != null
|
||||
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
||||
: "book",
|
||||
"title": "Első órádig:",
|
||||
"subtitle": "",
|
||||
"description": "",
|
||||
"startDate": storeFirstRunDate != null ? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) - (_delay.inMilliseconds)).toString(): "",
|
||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||
_delay.inMilliseconds)
|
||||
.toString(),
|
||||
"nextSubject": nextLesson != null
|
||||
? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||
: "",
|
||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||
};
|
||||
|
||||
case LiveCardState.night:
|
||||
return {
|
||||
"color":
|
||||
'#${_settings.liveActivityColor.toString().substring(10, 16)}',
|
||||
"icon": nextLesson != null
|
||||
? SubjectIcon.resolveName(subject: nextLesson?.subject)
|
||||
: "book",
|
||||
"title": "Első órádig:",
|
||||
"subtitle": "",
|
||||
"description": "",
|
||||
"startDate": storeFirstRunDate != null ? ((storeFirstRunDate?.millisecondsSinceEpoch ?? 0) - (_delay.inMilliseconds)).toString(): "",
|
||||
"endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) -
|
||||
_delay.inMilliseconds)
|
||||
.toString(),
|
||||
"nextSubject": nextLesson != null
|
||||
? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||
: "",
|
||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||
};
|
||||
|
||||
case LiveCardState.duringLesson:
|
||||
return {
|
||||
"color":
|
||||
@ -120,9 +154,7 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
"index":
|
||||
currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "",
|
||||
"title": currentLesson != null
|
||||
? currentLesson?.subject.renamedTo ??
|
||||
ShortSubject.resolve(subject: currentLesson?.subject)
|
||||
.capital()
|
||||
? currentLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: currentLesson?.subject).capital()
|
||||
: "",
|
||||
"subtitle": currentLesson?.room.replaceAll("_", " ") ?? "",
|
||||
"description": currentLesson?.description ?? "",
|
||||
@ -133,8 +165,7 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
_delay.inMilliseconds)
|
||||
.toString(),
|
||||
"nextSubject": nextLesson != null
|
||||
? nextLesson?.subject.renamedTo ??
|
||||
ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||
? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||
: "",
|
||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||
};
|
||||
@ -163,9 +194,7 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
_delay.inMilliseconds)
|
||||
.toString(),
|
||||
"nextSubject": (nextLesson != null
|
||||
? nextLesson?.subject.renamedTo ??
|
||||
ShortSubject.resolve(subject: nextLesson?.subject)
|
||||
.capital()
|
||||
? nextLesson?.subject.renamedTo ?? ShortSubject.resolve(subject: nextLesson?.subject).capital()
|
||||
: "")
|
||||
.capital(),
|
||||
"nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "",
|
||||
@ -178,37 +207,6 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void update() async {
|
||||
if (Platform.isIOS) {
|
||||
_liveActivitiesPlugin.areActivitiesEnabled().then((value) {
|
||||
if (value) {
|
||||
final cmap = toMap();
|
||||
if (!mapEquals(cmap, _lastActivity)) {
|
||||
_lastActivity = cmap;
|
||||
try {
|
||||
if (_lastActivity.isNotEmpty) {
|
||||
if (_latestActivityId == null) {
|
||||
_liveActivitiesPlugin
|
||||
.createActivity(_lastActivity)
|
||||
.then((value) => _latestActivityId = value);
|
||||
} else {
|
||||
_liveActivitiesPlugin.updateActivity(
|
||||
_latestActivityId!, _lastActivity);
|
||||
}
|
||||
} else {
|
||||
if (_latestActivityId != null) {
|
||||
_liveActivitiesPlugin.endActivity(_latestActivityId!);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('ERROR: Unable to create or update iOS LiveActivity!');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<Lesson> today = _today(_timetable);
|
||||
|
||||
if (today.isEmpty && !_hasCheckedTimetable) {
|
||||
@ -221,7 +219,14 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
? Duration(seconds: _settings.bellDelay)
|
||||
: Duration.zero;
|
||||
|
||||
final now = _now().add(_delay);
|
||||
|
||||
DateTime now = _now().add(_delay);
|
||||
|
||||
if ((currentState == LiveCardState.morning ||
|
||||
currentState == LiveCardState.afternoon ||
|
||||
currentState == LiveCardState.night) && storeFirstRunDate == null) {
|
||||
storeFirstRunDate = now;
|
||||
}
|
||||
|
||||
// Filter cancelled lessons #20
|
||||
// Filter label lessons #128
|
||||
@ -283,11 +288,65 @@ class LiveCardProvider extends ChangeNotifier {
|
||||
currentState = LiveCardState.empty;
|
||||
}
|
||||
|
||||
//LIVE ACTIVITIES
|
||||
|
||||
//CREATE
|
||||
if (!hasActivityStarted && nextLesson != null && nextLesson!
|
||||
.start
|
||||
.difference(now)
|
||||
.inMinutes <= 60 && (currentState == LiveCardState.morning ||
|
||||
currentState == LiveCardState.afternoon ||
|
||||
currentState == LiveCardState.night)) {
|
||||
debugPrint(
|
||||
"Az első óra előtt állunk, kevesebb mint egy órával. Létrehozás...");
|
||||
PlatformChannel.createLiveActivity(toMap());
|
||||
hasActivityStarted = true;
|
||||
}
|
||||
else if (!hasActivityStarted && ((currentState == LiveCardState.duringLesson &&
|
||||
currentLesson != null) ||
|
||||
currentState == LiveCardState.duringBreak)) {
|
||||
debugPrint(
|
||||
"Óra van, vagy szünet, de nincs LiveActivity. létrehozás...");
|
||||
PlatformChannel.createLiveActivity(toMap());
|
||||
hasActivityStarted = true;
|
||||
}
|
||||
|
||||
//UPDATE
|
||||
else if (hasActivityStarted) {
|
||||
if (hasActivitySettingsChanged) {
|
||||
debugPrint("Valamelyik beállítás megváltozott. Frissítés...");
|
||||
PlatformChannel.updateLiveActivity(toMap());
|
||||
hasActivitySettingsChanged = false;
|
||||
}
|
||||
else if (nextLesson != null || currentLesson != null) {
|
||||
bool afterPrevLessonEnd = prevLesson != null &&
|
||||
now.subtract(const Duration(seconds: 1)).isBefore(
|
||||
prevLesson!.end) && now.isAfter(prevLesson!.end);
|
||||
|
||||
bool afterCurrentLessonStart = currentLesson != null &&
|
||||
now.subtract(const Duration(seconds: 1)).isBefore(
|
||||
currentLesson!.start) && now.isAfter(currentLesson!.start);
|
||||
if (afterPrevLessonEnd || afterCurrentLessonStart) {
|
||||
debugPrint(
|
||||
"Óra kezdete/vége után 1 másodperccel vagyunk. Frissítés...");
|
||||
PlatformChannel.updateLiveActivity(toMap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//END
|
||||
if (hasActivityStarted && !hasDayEnd && nextLesson == null &&
|
||||
now.isAfter(prevLesson!.end)) {
|
||||
debugPrint("Az utolsó óra véget ért. Befejezés...");
|
||||
PlatformChannel.endLiveActivity();
|
||||
hasDayEnd = true;
|
||||
hasActivityStarted = false;
|
||||
}
|
||||
LAData = toMap();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool get show => currentState != LiveCardState.empty;
|
||||
|
||||
Duration get delay => _delay;
|
||||
|
||||
bool _sameDate(DateTime a, DateTime b) =>
|
||||
|
43
refilc/lib/api/providers/liveactivity/platform_channel.dart
Normal file
43
refilc/lib/api/providers/liveactivity/platform_channel.dart
Normal file
@ -0,0 +1,43 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class PlatformChannel {
|
||||
static const MethodChannel _channel = MethodChannel('hu.refilc/liveactivity');
|
||||
|
||||
static Future<void> createLiveActivity(
|
||||
Map<String, dynamic> activityData) async {
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
debugPrint("creating...");
|
||||
await _channel.invokeMethod('createLiveActivity', activityData);
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint("Hiba történt a Live Activity létrehozásakor: ${e.message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> updateLiveActivity(
|
||||
Map<String, dynamic> activityData) async {
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
debugPrint("updating...");
|
||||
await _channel.invokeMethod('updateLiveActivity', activityData);
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint("Hiba történt a Live Activity frissítésekor: ${e.message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> endLiveActivity() async {
|
||||
if (Platform.isIOS) {
|
||||
try {
|
||||
debugPrint("finishing...");
|
||||
await _channel.invokeMethod('endLiveActivity');
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint("Hiba történt a Live Activity befejezésekor: ${e.message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,9 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:home_widget/home_widget.dart';
|
||||
|
||||
import 'live_card_provider.dart';
|
||||
import 'liveactivity/platform_channel.dart';
|
||||
|
||||
// Mutex
|
||||
bool lock = false;
|
||||
|
||||
@ -86,10 +89,17 @@ Future<void> syncAll(BuildContext context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Future.wait(tasks).then((value) {
|
||||
// Unlock
|
||||
lock = false;
|
||||
|
||||
if(Platform.isIOS && LiveCardProvider.hasActivityStarted == true){
|
||||
PlatformChannel.endLiveActivity();
|
||||
LiveCardProvider.hasActivityStarted = false;
|
||||
}
|
||||
|
||||
// Update Widget
|
||||
if (Platform.isAndroid) updateWidget();
|
||||
});
|
||||
|
15
refilc/lib/helpers/live_activity_helper.dart
Normal file
15
refilc/lib/helpers/live_activity_helper.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:refilc/api/providers/live_card_provider.dart';
|
||||
import '../api/providers/liveactivity/platform_channel.dart';
|
||||
|
||||
|
||||
class LiveActivityHelper {
|
||||
@pragma('vm:entry-point')
|
||||
void backgroundJob() async {
|
||||
// initialize provider
|
||||
if (!LiveCardProvider.hasDayEnd) {
|
||||
await PlatformChannel.updateLiveActivity(LiveCardProvider.LAData);
|
||||
} else {
|
||||
await PlatformChannel.endLiveActivity();
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ import 'package:refilc_mobile_ui/screens/error_screen.dart';
|
||||
import 'package:refilc_mobile_ui/screens/error_report_screen.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
import 'helpers/live_activity_helper.dart';
|
||||
|
||||
// days without touching grass: 5,843 (16 yrs)
|
||||
|
||||
void main() async {
|
||||
@ -84,6 +86,7 @@ class Startup {
|
||||
// Notifications setup
|
||||
if (!kIsWeb) {
|
||||
initPlatformState();
|
||||
initAdditionalBackgroundFetch();
|
||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
}
|
||||
|
||||
@ -196,7 +199,12 @@ Future<void> initPlatformState() async {
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] Event received $taskId");
|
||||
}
|
||||
if (taskId == "com.transistorsoft.refilcliveactivity") {
|
||||
if (!Platform.isIOS) return;
|
||||
LiveActivityHelper().backgroundJob();
|
||||
} else {
|
||||
NotificationsHelper().backgroundJob();
|
||||
}
|
||||
BackgroundFetch.finish(taskId);
|
||||
}, (String taskId) async {
|
||||
// <-- Task timeout handler.
|
||||
@ -231,6 +239,50 @@ void backgroundHeadlessTask(HeadlessTask task) {
|
||||
if (kDebugMode) {
|
||||
print('[BackgroundFetch] Headless event received.');
|
||||
}
|
||||
if (taskId == "com.transistorsoft.refilcliveactivity") {
|
||||
if (!Platform.isIOS) return;
|
||||
LiveActivityHelper().backgroundJob();
|
||||
} else {
|
||||
NotificationsHelper().backgroundJob();
|
||||
BackgroundFetch.finish(task.taskId);
|
||||
} BackgroundFetch.finish(task.taskId);
|
||||
}
|
||||
|
||||
Future<void> initAdditionalBackgroundFetch() async {
|
||||
int status = await BackgroundFetch.configure(
|
||||
BackgroundFetchConfig(
|
||||
minimumFetchInterval: 1, // 1 minute
|
||||
stopOnTerminate: false,
|
||||
enableHeadless: true,
|
||||
requiresBatteryNotLow: false,
|
||||
requiresCharging: false,
|
||||
requiresStorageNotLow: false,
|
||||
requiresDeviceIdle: false,
|
||||
requiredNetworkType: NetworkType.ANY,
|
||||
startOnBoot: true), (String taskId) async {
|
||||
// <-- Event handler
|
||||
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] Event received $taskId");
|
||||
}
|
||||
LiveActivityHelper liveActivityHelper = LiveActivityHelper();
|
||||
liveActivityHelper.backgroundJob();
|
||||
|
||||
BackgroundFetch.finish(taskId);
|
||||
}, (String taskId) async {
|
||||
// <-- Task timeout handler.
|
||||
if (kDebugMode) {
|
||||
print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
|
||||
}
|
||||
BackgroundFetch.finish(taskId);
|
||||
});
|
||||
if (kDebugMode) {
|
||||
print('[BackgroundFetch] configure success: $status');
|
||||
}
|
||||
BackgroundFetch.scheduleTask(TaskConfig(
|
||||
taskId: "com.transistorsoft.refilcliveactivity",
|
||||
delay: 300000, // 5 minute
|
||||
periodic: true,
|
||||
forceAlarmManager: true,
|
||||
stopOnTerminate: false,
|
||||
enableHeadless: true));
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:refilc/api/providers/database_provider.dart';
|
||||
@ -10,6 +11,8 @@ import 'package:refilc/theme/colors/dark_mobile.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../api/providers/live_card_provider.dart';
|
||||
|
||||
enum Pages { home, grades, timetable, notes, absences }
|
||||
|
||||
enum UpdateChannel { stable, beta, dev }
|
||||
@ -666,6 +669,9 @@ class SettingsProvider extends ChangeNotifier {
|
||||
if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay;
|
||||
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) {
|
||||
_bellDelayEnabled = bellDelayEnabled;
|
||||
if(Platform.isIOS){
|
||||
LiveCardProvider.hasActivitySettingsChanged = true;
|
||||
}
|
||||
}
|
||||
if (gradeOpeningFun != null && gradeOpeningFun != _gradeOpeningFun) {
|
||||
_gradeOpeningFun = gradeOpeningFun;
|
||||
|
@ -4,6 +4,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:refilc/api/providers/database_provider.dart';
|
||||
import 'package:refilc/api/providers/live_card_provider.dart';
|
||||
import 'package:refilc/api/providers/user_provider.dart';
|
||||
import 'package:refilc/helpers/quick_actions.dart';
|
||||
import 'package:refilc/models/settings.dart';
|
||||
@ -750,6 +751,9 @@ class _BellDelaySettingState extends State<BellDelaySetting>
|
||||
Provider.of<SettingsProvider>(context, listen: false)
|
||||
.update(bellDelay: currentDelay.inSeconds);
|
||||
_tabController.index = currentDelay.inSeconds > 0 ? 1 : 0;
|
||||
if(Platform.isIOS){
|
||||
LiveCardProvider.hasActivitySettingsChanged = true;
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
@ -760,6 +764,9 @@ class _BellDelaySettingState extends State<BellDelaySetting>
|
||||
//Provider.of<SettingsProvider>(context, listen: false).update(context, rounding: (r * 10).toInt());
|
||||
Provider.of<SettingsProvider>(context, listen: false)
|
||||
.update(bellDelay: currentDelay.inSeconds);
|
||||
if(Platform.isIOS){
|
||||
LiveCardProvider.hasActivitySettingsChanged = true;
|
||||
}
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
),
|
||||
@ -897,6 +904,7 @@ class _LiveActivityColorSettingState extends State<LiveActivityColorSetting> {
|
||||
currentColor = k as Color;
|
||||
settings.update(
|
||||
liveActivityColor: currentColor.withAlpha(255));
|
||||
LiveCardProvider.hasActivitySettingsChanged = true;
|
||||
Navigator.of(context).maybePop();
|
||||
});
|
||||
},
|
||||
@ -913,6 +921,7 @@ class _LiveActivityColorSettingState extends State<LiveActivityColorSetting> {
|
||||
var defaultColors =
|
||||
SettingsProvider.defaultSettings().liveActivityColor;
|
||||
settings.update(liveActivityColor: defaultColors);
|
||||
LiveCardProvider.hasActivitySettingsChanged = true;
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
child: Text(SettingsLocalization("reset").i18n),
|
||||
|
Loading…
x
Reference in New Issue
Block a user