mirror of
https://invent.kde.org/network/kdeconnect-ios.git
synced 2025-12-12 20:36:04 +01:00
feat: iOS background exec through location
ui: SettingsBackgroundView
This commit is contained in:
@@ -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 */,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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")) {
|
||||
|
||||
Reference in New Issue
Block a user