feat: iOS background exec through location

ui: SettingsBackgroundView
This commit is contained in:
Ruixuan Tu
2022-09-23 17:08:46 -05:00
parent aa8f7ba120
commit f1ef1b11e4
4 changed files with 190 additions and 0 deletions

View File

@@ -28,6 +28,7 @@
53A92E6D27ED4F4F0085A10C /* SystemSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A92E6C27ED4F4F0085A10C /* SystemSound.swift */; };
5EA4114427960C1C0044C559 /* libs.json in Resources */ = {isa = PBXBuildFile; fileRef = 5EA4114227960C1C0044C559 /* libs.json */; };
5EA4114627960D0B0044C559 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EA4114527960D0B0044C559 /* Assets.xcassets */; };
5EEC17B928DE3731001C7BC4 /* SettingsBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EEC17B828DE3731001C7BC4 /* SettingsBackgroundView.swift */; };
5EF3BDAB27995FCE005C2E3A /* iOS14CompatibleTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EF3BDAA27995FCE005C2E3A /* iOS14CompatibleTextView.swift */; };
5EFA051C279432CA009C91D2 /* SettingsAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFA051B279432CA009C91D2 /* SettingsAboutView.swift */; };
5EFFF3082D1B6F3000A3EFCA /* InternalBattery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFFF3032D1B6F3000A3EFCA /* InternalBattery.swift */; };
@@ -150,6 +151,7 @@
53A92E6C27ED4F4F0085A10C /* SystemSound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemSound.swift; sourceTree = "<group>"; };
5EA4114227960C1C0044C559 /* libs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = libs.json; sourceTree = "<group>"; };
5EA4114527960D0B0044C559 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5EEC17B828DE3731001C7BC4 /* SettingsBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBackgroundView.swift; sourceTree = "<group>"; };
5EF3BDAA27995FCE005C2E3A /* iOS14CompatibleTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOS14CompatibleTextView.swift; sourceTree = "<group>"; };
5EFA051B279432CA009C91D2 /* SettingsAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAboutView.swift; sourceTree = "<group>"; };
5EFFF3022D1B6F3000A3EFCA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -564,6 +566,7 @@
5EFA051B279432CA009C91D2 /* SettingsAboutView.swift */,
D20922C827C5EC3900AF6F76 /* FeaturesList.swift */,
D264C2DA27C8667800BD8AFB /* AppIconPicker.swift */,
5EEC17B828DE3731001C7BC4 /* SettingsBackgroundView.swift */,
);
path = Settings;
sourceTree = "<group>";
@@ -826,6 +829,7 @@
D24FFB4D29E76E8500604DA4 /* FilesTab.swift in Sources */,
D20922C927C5EC3900AF6F76 /* FeaturesList.swift in Sources */,
A0BECF6826C195240037E299 /* ConnectedDevicesViewModel.swift in Sources */,
5EEC17B928DE3731001C7BC4 /* SettingsBackgroundView.swift in Sources */,
53A0A1DE283ED78700C7C473 /* LocalizedStringKey+Extensions.swift in Sources */,
D24FFB4F29E7854000604DA4 /* OpenReceivedDocumentsFolderButton.swift in Sources */,
D27D727F29B051D8002C00B7 /* NetworkChangeMonitor.swift in Sources */,

View File

@@ -43,6 +43,15 @@
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>KDE Connect will be able to run in background if be granted always location access</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>KDE Connect will be able to run in background if be granted always location access
Please proceed to grant always location access</string>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchScreen</key>

View File

@@ -0,0 +1,169 @@
//
// SettingsBackgroundView.swift
// KDE Connect
//
// Created by Ruixuan Tu on 2022/09/23.
//
import SwiftUI
import CoreLocation.CLLocationManager
class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var authorizationStatus: CLAuthorizationStatus
@Published var typeStr: String = "Unknown"
@Published var typeIconName: String = "questionmark.circle.fill"
@Published var typeIconColor: Color = .yellow
private let locationManager: CLLocationManager
override init() {
locationManager = CLLocationManager()
self.authorizationStatus = locationManager.authorizationStatus
super.init()
self.updateStates(authorizationStatus)
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.allowsBackgroundLocationUpdates = true
locationManager.showsBackgroundLocationIndicator = true
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
self.updateStates(manager.authorizationStatus)
}
private func updateStates(_ status: CLAuthorizationStatus) {
self.authorizationStatus = authorizationStatus
typeStr = self.getTypeStr(status)
typeIconName = self.getTypeIconName(status)
typeIconColor = self.getTypeIconColor(status)
}
public func requestWhenInUse() {
return locationManager.requestWhenInUseAuthorization()
}
public func requestAlways() {
return locationManager.requestAlwaysAuthorization()
}
public func start() {
stop()
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
}
public func stop() {
locationManager.stopUpdatingLocation()
locationManager.stopMonitoringSignificantLocationChanges()
}
private func getTypeStr(_ status: CLAuthorizationStatus) -> String {
switch status {
case .authorizedAlways:
return "Always"
case .authorizedWhenInUse:
return "When In Use"
case .notDetermined:
return "Not Determined"
case .denied:
return "Denied"
default:
return "Unknown"
}
}
private func getTypeIconName(_ status: CLAuthorizationStatus) -> String {
switch status {
case .authorizedAlways:
return "checkmark.circle.fill"
case .authorizedWhenInUse:
return "circle.righthalf.filled"
case .notDetermined:
return "circle.dashed"
case .denied:
return "exclamationmark.circle.fill"
default:
return "questionmark.circle.fill"
}
}
private func getTypeIconColor(_ status: CLAuthorizationStatus) -> Color {
switch status {
case .authorizedAlways:
return .green
case .denied:
return .red
default:
return .yellow
}
}
}
struct SettingsBackgroundView: View {
@StateObject var locationViewModel = LocationViewModel()
var body: some View {
List {
Section {
Label {
Text(locationViewModel.typeStr)
} icon: {
Image(systemName: locationViewModel.typeIconName).foregroundColor(locationViewModel.typeIconColor)
}
} header: {
Text("Current Location Permission")
}
Section {
Button {
locationViewModel.requestWhenInUse()
} label: {
Label("When In Use", systemImage: "1.circle")
.labelStyle(.accessibilityTitleOnly)
.accentColor(.primary)
}
Button {
locationViewModel.requestAlways()
} label: {
Label("Always", systemImage: "2.circle")
.labelStyle(.accessibilityTitleOnly)
.accentColor(.primary)
}
} header: {
Text("Grant Location Permission for")
} footer: {
VStack {
Text("""
KDE Connect will be able to run in background if be granted always location access\n\n\
To grant always permission, grant When In Use by Button 1 then Always by Button 2, or grant When In Use by Button 2 and change in Settings app
""")
}
}
Section {
Button {
locationViewModel.start()
} label: {
Label("Start Location Update", systemImage: "play")
.labelStyle(.accessibilityTitleOnly)
.accentColor(.primary)
}
Button {
locationViewModel.stop()
} label: {
Label("Stop Location Update", systemImage: "stop")
.labelStyle(.accessibilityTitleOnly)
.accentColor(.primary)
}
} header: {
Text("Control")
} footer: {
Text("KDE Connect will run in background if location update has been started")
}
}.navigationTitle("Background Settings")
}
}
struct SettingsBackgroundView_Previews: PreviewProvider {
static var previews: some View {
SettingsBackgroundView()
}
}

View File

@@ -60,6 +60,14 @@ struct SettingsView: View {
.labelStyle(.accessibilityTitleOnly)
.accentColor(.primary)
}
NavigationLink {
SettingsBackgroundView()
} label: {
Label("Background Execution", systemImage: "pin")
.labelStyle(.accessibilityTitleOnly)
.accentColor(.primary)
}
}
Section(header: Text("Information")) {