Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best effort to reflect state in custom widget after interaction #3415

Merged
merged 6 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions HomeAssistant.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@
422F88192D42989E00706A0A /* CustomWidgetPressButtonAppIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422F88182D42989E00706A0A /* CustomWidgetPressButtonAppIntent.swift */; };
422F881A2D42989E00706A0A /* CustomWidgetPressButtonAppIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422F88182D42989E00706A0A /* CustomWidgetPressButtonAppIntent.swift */; };
422F951F2CFDF7C5003B7514 /* HAApplicationShortcutItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422F951E2CFDF7C5003B7514 /* HAApplicationShortcutItem.swift */; };
423179802D54FADD0037A8A4 /* AppIntentHaptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4231797F2D54FADD0037A8A4 /* AppIntentHaptics.swift */; };
423179812D54FADD0037A8A4 /* AppIntentHaptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4231797F2D54FADD0037A8A4 /* AppIntentHaptics.swift */; };
42333ADB2D0B1771001E8408 /* EntityRegistryListForDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42333ADA2D0B1771001E8408 /* EntityRegistryListForDisplay.swift */; };
42333ADC2D0B1771001E8408 /* EntityRegistryListForDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42333ADA2D0B1771001E8408 /* EntityRegistryListForDisplay.swift */; };
42333ADD2D0B1771001E8408 /* EntityRegistryListForDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42333ADA2D0B1771001E8408 /* EntityRegistryListForDisplay.swift */; };
Expand Down Expand Up @@ -1928,6 +1930,7 @@
422F88152D428E8300706A0A /* CustomWidgetActivateAppIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWidgetActivateAppIntent.swift; sourceTree = "<group>"; };
422F88182D42989E00706A0A /* CustomWidgetPressButtonAppIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWidgetPressButtonAppIntent.swift; sourceTree = "<group>"; };
422F951E2CFDF7C5003B7514 /* HAApplicationShortcutItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HAApplicationShortcutItem.swift; sourceTree = "<group>"; };
4231797F2D54FADD0037A8A4 /* AppIntentHaptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentHaptics.swift; sourceTree = "<group>"; };
42333ADA2D0B1771001E8408 /* EntityRegistryListForDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityRegistryListForDisplay.swift; sourceTree = "<group>"; };
4235075C2CDB756800A19902 /* HAServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HAServices.swift; sourceTree = "<group>"; };
4239D1802C4FFB75003497FC /* WatchUserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchUserDefaults.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3868,6 +3871,7 @@
422F88182D42989E00706A0A /* CustomWidgetPressButtonAppIntent.swift */,
42EF0ACC2D4CDC0C0088C91E /* ResetAllCustomWidgetConfirmationAppIntent.swift */,
42EF0ACF2D4CDFF30088C91E /* UpdateWidgetItemConfirmationStateAppIntent.swift */,
4231797F2D54FADD0037A8A4 /* AppIntentHaptics.swift */,
);
path = AppIntents;
sourceTree = "<group>";
Expand Down Expand Up @@ -6917,6 +6921,7 @@
4223688B2D40F9B7005911E4 /* WidgetCustom.swift in Sources */,
429BA2AF2C800CAB00A50996 /* SFSymbolEntity.swift in Sources */,
420461692C8F29440062E89F /* ControlLight.swift in Sources */,
423179812D54FADD0037A8A4 /* AppIntentHaptics.swift in Sources */,
110E694624E771AB004AA96D /* Color+Hex.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -7180,6 +7185,7 @@
42D3E4B72C5D2C2700444BE6 /* WidgetScriptsAppIntent.swift in Sources */,
4251AA9B2C6B9DBE004CCC9D /* MagicItemCustomizationViewModel.swift in Sources */,
422F88142D4282CD00706A0A /* CustomWidgetToggleAppIntent.swift in Sources */,
423179802D54FADD0037A8A4 /* AppIntentHaptics.swift in Sources */,
4273C48F2C885FB00065A5B4 /* SFSymbolEntity.swift in Sources */,
116D3A442724EFFB00EF5D21 /* OnboardingAuthTokenExchange.swift in Sources */,
429821142CD0DD85005ECD39 /* BluetoothPermissionViewModel.swift in Sources */,
Expand Down
5 changes: 1 addition & 4 deletions Sources/Extensions/AppIntents/Action/PerformAction.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import AppIntents
import AudioToolbox
import Foundation
import PromiseKit
import Shared
Expand Down Expand Up @@ -46,9 +45,7 @@ struct PerformAction: AppIntent, CustomIntentMigratedAppIntent, PredictableInten
}

if hapticConfirmation {
// Unfortunately this is the only 'haptics' that works with widgets
// ideally in the future this should use CoreHaptics for a better experience
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
AppIntentHaptics.notify()
}

try await withCheckedThrowingContinuation { continuation in
Expand Down
5 changes: 1 addition & 4 deletions Sources/Extensions/AppIntents/Script/ScriptAppIntent.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import AppIntents
import AudioToolbox
import Foundation
import PromiseKit
import SFSafeSymbols
Expand Down Expand Up @@ -37,9 +36,7 @@ final class ScriptAppIntent: AppIntent {

func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
if hapticConfirmation {
// Unfortunately this is the only 'haptics' that work with widgets
// ideally in the future this should use CoreHaptics for a better experience
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
AppIntentHaptics.notify()
}

let success: Bool = try await withCheckedThrowingContinuation { continuation in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AudioToolbox
import Foundation

enum AppIntentHaptics {
static func notify() {
// Unfortunately this is the only 'haptics' that work with widgets
// ideally in the future this should use CoreHaptics for a better experience
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ struct CustomWidgetActivateAppIntent: AppIntent {
static var title: LocalizedStringResource = "Activate"
static var isDiscoverable: Bool = false

// No translation needed below, this is not a discoverable intent
@Parameter(title: "Server")
var serverId: String?
@Parameter(title: "Domain")
Expand All @@ -27,6 +28,7 @@ struct CustomWidgetActivateAppIntent: AppIntent {
}), let connection = Current.api(for: server)?.connection else {
return .result()
}
AppIntentHaptics.notify()

guard let request: HATypedRequest<HAResponseVoid> = {
switch domain {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct CustomWidgetPressButtonAppIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle"
static var isDiscoverable: Bool = false

// No translation needed below, this is not a discoverable intent
@Parameter(title: "Server")
var serverId: String?
@Parameter(title: "Domain")
Expand All @@ -25,6 +26,7 @@ struct CustomWidgetPressButtonAppIntent: AppIntent {
}), let connection = Current.api(for: server)?.connection else {
return .result()
}
AppIntentHaptics.notify()
await withCheckedContinuation { continuation in
connection.send(.pressButton(domain: domain, entityId: entityId)).promise.pipe { result in
switch result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@ struct CustomWidgetToggleAppIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle"
static var isDiscoverable: Bool = false

// No translation needed below, this is not a discoverable intent
@Parameter(title: "Server")
var serverId: String?
@Parameter(title: "Domain")
var domain: String?
@Parameter(title: "Entity ID")
var entityId: String?
@Parameter(title: "Is widget showing states?")
var widgetShowingStates: Bool?

func perform() async throws -> some IntentResult {
guard let serverId,
let domainString = domain,
let domain = Domain(rawValue: domainString),
let entityId,
let widgetShowingStates,
let server = Current.servers.all.first(where: { server in
server.identifier.rawValue == serverId
}), let connection = Current.api(for: server)?.connection else {
return .result()
}
AppIntentHaptics.notify()
await withCheckedContinuation { continuation in
connection.send(.toggleDomain(domain: domain, entityId: entityId)).promise.pipe { result in
switch result {
Expand All @@ -47,6 +52,12 @@ struct CustomWidgetToggleAppIntent: AppIntent {
}
}
_ = try await ResetAllCustomWidgetConfirmationAppIntent().perform()
if widgetShowingStates {
/* Since when you toggle an entity not always it reflects the new state right away
and at the same time push notifications to update widgets are currently not working reliably
in iOS, this delay is out best effort for the user to see the correct state after finishing the interaction */
try await Task.sleep(nanoseconds: 1_000_000_000)
}
return .result()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import SwiftUI

@available(iOS 16.4, *)
struct ResetAllCustomWidgetConfirmationAppIntent: AppIntent {
// No translation needed below, this is not a discoverable intent
static var title: LocalizedStringResource = "Reset custom widget confirmation states"
static var isDiscoverable: Bool = false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct UpdateWidgetItemConfirmationStateAppIntent: AppIntent {
static var title: LocalizedStringResource = "Update custom widget confirmation"
static var isDiscoverable: Bool = false

// No translation needed below, this is not a discoverable intent
@Parameter(title: "Widget Id")
var widgetId: String?

Expand Down
5 changes: 1 addition & 4 deletions Sources/Extensions/Widgets/Scene/SceneAppIntent.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import AppIntents
import AudioToolbox
import Foundation
import PromiseKit
import Shared
Expand Down Expand Up @@ -36,9 +35,7 @@ final class SceneAppIntent: AppIntent {

func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
if hapticConfirmation {
// Unfortunately this is the only 'haptics' that work with widgets
// ideally in the future this should use CoreHaptics for a better experience
AudioServicesPlayAlertSound(SystemSoundID(kSystemSoundID_Vibrate))
AppIntentHaptics.notify()
}

let success: Bool = try await withCheckedThrowingContinuation { continuation in
Expand Down
Loading