Skip to content
Snippets Groups Projects
Commit 9680b0db authored by miklosimate's avatar miklosimate
Browse files

Initial Commit

parents
No related branches found
No related tags found
No related merge requests found
Showing
with 596 additions and 0 deletions
LiftMeApp.png

737 KiB

This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "8FF16691-DC7A-493A-BE49-92307474C2FE"
type = "1"
version = "2.0">
</Bucket>
<?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>SchemeUserState</key>
<dict>
<key>SenzorDataCollectorApp.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "LiftMeApp.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
SenzorDataCollectorApp/Assets.xcassets/AppIcon.appiconset/LiftMeApp.png

737 KiB

{
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// ContentView.swift
// SensorDataCollectorApp
//
// Created by Miklósi Máté on 2023. 04. 29..
//
import SwiftUI
import CoreData
import CoreMotion
import UniformTypeIdentifiers
import MobileCoreServices
import SensorKit
struct ContentView: View {
//to be able to choose file location
@State var showingFilePicker = false
// The URL of the selected file
@State var selectedFolder: URL?
@State var activeSensorsList: [MeasuringSensors] = []
//init motion manager - to record all sensors data
let motionManager = CMMotionManager()
let altimeter = CMAltimeter()
@State var motionAuthorizationStatus: CMAuthorizationStatus = .notDetermined
@State var countdown: Int = 5
@State var timer: Timer?
@State var dataColl = false
@StateObject var sensor = SensorCollector(activeSensorList: [])
@State var fileLocations: [String] = []
@State var MeasurementID: Int
@ObservedObject var notificationManager = NotificationManager()
var notificationObserver = UserNotificationObserver.shared
let activityManager = CMMotionActivityManager()
//let observer = NotificationObserver()
var body: some View {
NavigationView {
VStack{
ForEach($sensor.activeSensorsList, id: \.self) { $list in
HStack{
Text(list.sensorName)
Spacer()
Text(String(list.sensorData.count))
NavigationLink(
destination: SensorDetailView(
x: $list.sensorData.last?.1.x ?? .constant(0.0),
y: $list.sensorData.last?.1.y ?? .constant(0.0),
z: $list.sensorData.last?.1.z ?? .constant(0.0), sensorName: $list.sensorName ),
label: { Text("Details") }
)
}
}
.padding()
//Toggle on - off the data collection:
Toggle("data collection:",isOn: $dataColl)
.padding()
.onChange(of: dataColl){value in
if value {
sensor.startAllRecording(motionManager: motionManager, altimeter: altimeter)
}else{//if we toggled off the toggle:
//stop reading from sensors
sensor.stopAllRecording(motionManager: motionManager, altimeter: altimeter)
}
}
//--------------------- notification test -----------------
Button(action: {
let sender = NotificationSender()
sender.sendNotification(title: "Hello, world!", body: "This is a local notification", delay: Double(countdown))
// timer - to display the remaining time until notification
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
if countdown > 0 {
countdown -= 1
} else {
timer.invalidate()
countdown = 5
}
}
}) {
Text("Send Notification - in: \(countdown)")
}
.padding()
List(notificationManager.notifications, id: \.identifier) { notification in
Text(notification.content.title)
}
//---------------- - --- select the location of the saved files -------------
Button("Select destination") {
showingFilePicker = true
}
.padding()
.fileImporter(isPresented: $showingFilePicker, allowedContentTypes: [.folder], allowsMultipleSelection: false) { result in
// Handle the user-selected file or folder
switch result {
case .success(let urls):
// Check if any URLs were selected
if let url = urls.first {
selectedFolder = url // Store the selected folder in selectedFolder
}
case .failure(let error):
print("Error selecting folder: \(error.localizedDescription)")
}
}
//dispaly the selected path, if there is a selected path
if let selectedFolder = selectedFolder {
Text("Selected folder: \(selectedFolder.path)")
.padding()
} else {
Text("No folder selected")
.padding()
}
// ------------------------------- save to json ----------------------------
Button("Save All to JSON") {
do {
//calculate timestamps:
let now = Date()
let timeInMiliseconds = Int(now.timeIntervalSince1970 * 1000)
if let selectedFolder = selectedFolder {
try sensor.exportAllSensorToJson(fileLocations: &fileLocations, filename_prefix: String(MeasurementID) + String(timeInMiliseconds), fileLocationURL: selectedFolder, deviceID: "-")
} else {
print("Please select a folder to save the JSON file.")
}
} catch {
print("Error exporting to JSON: \(error.localizedDescription)")
}
}
.disabled(selectedFolder == nil)
ScrollView{
VStack{
ForEach(fileLocations, id: \.self) { location in
Text(location)
}
}
}
.frame(maxHeight: 200)
}.onAppear{
//let notificationObserver = UserNotificationObserver.shared
notificationObserver.startObservingNotifications()
notificationManager.requestNotificationPermissions()
notificationManager.observeNotifications()
print("init!")
}
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
// This code will be executed when the user unlocks their device
print("Device unlocked!")
self.dataColl = false
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(MeasurementID: 1).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
//
// ExportToJson.swift
// SenzorDataCollectorApp
//
// Created by Miklósi Máté on 2023. 04. 29..
//
import Foundation
import SwiftUI
import Foundation
import UniformTypeIdentifiers
import UIKit
//calls crate jsonfile to the specific sensor - sensorData - has to be the name of the list where the data is stored
//called by Sensors -> exportAllSensorToJson
func exportToJSON(fileLocations: inout [String],
sensorData: [(timeInMiliSec: Int, (x: Double, y: Double, z: Double))],
fileName: String,
sensorName: String,
deviceID: String,
fileLocationURL: URL) throws {
try fileLocations.append(createJSONFile(FileName: fileName, SensorName: sensorName, Data: sensorData,fileLocationURL: fileLocationURL, deviceID: deviceID))
}
//create the json file
func createJSONFile(FileName: String, SensorName: String, Data: [(timeInMiliSec: Int, (x: Double, y: Double, z: Double))], fileLocationURL: URL, deviceID: String) throws -> String {
let jsonData: [[String: Any]] = Data.map { tuple in
return [
"timestamp": tuple.0,
"values": [tuple.1.x, tuple.1.y, tuple.1.z]
]
}
let json: [Any] = [
"ios.\(SensorName)_vector",
"\(deviceID)",
jsonData
]
let jsonDataObj = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
// Request permission to access the file URL
do {
try fileLocationURL.startAccessingSecurityScopedResource()
// Now you can access the file at `selectedFileURL`.
let fileURL = fileLocationURL.appendingPathComponent("\(FileName).json")
try jsonDataObj.write(to: fileURL, options: .atomic)
fileLocationURL.stopAccessingSecurityScopedResource()
} catch {
print("Error accessing file: \(error)")
}
fileLocationURL.stopAccessingSecurityScopedResource()
return fileLocationURL.absoluteString
}
func returnMeasurementJson() -> [String]{
//TODO
let now = Date()
let timeInMiliSec = Int(now.timeIntervalSince1970 * 1000)
let returnVal = ["{}"]
return returnVal
}
//
// FilePicker.swift
// SenzorDataCollectorApp
//
// Created by Miklósi Máté on 2023. 04. 29..
//
// THIS IS JUST A PLAYGROUND FILE, TO TEST VIEWS
import SwiftUI
import UIKit
struct FilePicker: View {
@State private var showingDocumentPicker = false
@State private var selectedFileURL: URL?
var body: some View {
VStack {
Button("Select File") {
showingDocumentPicker = true
}
}
}
}
struct FilePicker_Previews: PreviewProvider {
static var previews: some View {
FilePicker()
}
}
<?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>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>hu.itk.ppke.backgroundTask</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
<string>remote-notification</string>
</array>
</dict>
</plist>
//
// NotificationObserver.swift
// SenzorDataCollectorApp
//
// Created by Miklósi Máté on 08/11/2023.
//
import Foundation
//
//class NotificationObserver {
// init() {
// NotificationCenter.default.addObserver(self, selector: #selector(handleNotification(_:)), name: nil, object: nil)
// }
//
// deinit {
// NotificationCenter.default.removeObserver(self)
// }
//
// @objc func handleNotification(_ notification: Notification) {
// // Print out notification details to the console
// //print("Received notification: \(notification.name.rawValue)")
//
// if let userInfo = notification.userInfo {
// for (key, value) in userInfo {
// print("\(key): \(value)")
// }
// }
// }
//}
//
//// Usage
//let observer = NotificationObserver()
// To stop observing notifications (optional)
// observer = nil
//
// NotificationSender.swift
// SenzorDataCollectorApp
//
// Created by Miklósi Máté on 2023. 04. 29..
//
//
import Foundation
import UserNotifications
class NotificationSender {
//we can call NotifictaionSender.sendNotification()
func sendNotification(title: String, body: String, delay: Double) {
// Request permission to send notifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
if granted {
// Create the notification content
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
content.interruptionLevel = .timeSensitive
// Set the notification trigger
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: delay, repeats: false)
// Create the notification request
let request = UNNotificationRequest(identifier: "local_notification", content: content, trigger: trigger)
// Add the notification request to the notification center
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error sending local notification: \(error.localizedDescription)")
} else {
print("Local notification sent successfully")
}
}
} else {
print("Permission denied for sending notifications")
}
}
}
}
import UserNotifications
import UIKit
class NotificationManager: ObservableObject {
@Published var notifications: [UNNotificationRequest] = []
func requestNotificationPermissions() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
print("Notification permissions granted.")
} else if let error = error {
print("Error requesting notification permissions: \(error)")
}
}
}
func observeNotifications() {
UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
DispatchQueue.main.async {
self.notifications = requests
}
}
NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { _ in
UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
DispatchQueue.main.async {
self.notifications = requests
}
}
}
}
}
//
// Persistence.swift
// SenzorDataCollectorApp
//
// Created by Miklósi Máté on 2023. 04. 29..
//
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
for _ in 0..<10 {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
}
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "SenzorDataCollectorApp")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
}
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// SensorDataCollectorAppApp.swift
// SensorDataCollectorApp
//
// Created by Miklósi Máté on 2023. 04. 29..
//
import SwiftUI
@main
struct SensorDataCollectorAppApp: App {
//background fetching is only possible here
@StateObject var timer: CustomTimer = .init()
@State var lastActiveTimeStamp: Date = Date()
let persistenceController = PersistenceController.shared
@Environment(\.scenePhase) var phase
var body: some Scene {
WindowGroup {
StartingView(customTimer: timer)
// ContentView(MeasurementID: 1)
.environment(\.managedObjectContext, persistenceController.container.viewContext)
.environmentObject(timer)
}
.onChange(of: phase ) { newValue in
if timer.isStarted{
if newValue == .background {
lastActiveTimeStamp = Date()
}
if newValue == .active {
let TimeDifference = Date().timeIntervalSince(lastActiveTimeStamp)
if timer.totalSeconds - Int(TimeDifference) <= 0 {
timer.isStarted = false
timer.totalSeconds = 0
timer.isFinished = true
}else{
timer.totalSeconds -= Int(TimeDifference)
}
}
}
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment