How to handle Siri interruption on Apple TV - tvos

I'm looking for a way to handle Siri interruptions on the AppleTV.
According to this guide: https://developer.apple.com/documentation/avfoundation/avaudiosession/responding_to_audio_session_interruptions it should be possible to get notifications of an audio session interruption, but this doesn't seem to work.
I have tried
NotificationCenter.default.addObserver(
self,
selector: #selector(handleInterruption(_:)),
name: .AVAudioSessionInterruption,
object: nil)
The callback never occurs.
What i would like is to pause video while the user is using Siri and then resume when they have finished.

internal func registerToHandleInterruptions() {
// When Siri interrupts playback a UIApplicationWillResignActive notification will be triggered
NotificationCenter.default.addObserver(
self,
selector: #selector(handleInterruption(_:)),
name: .UIApplicationWillResignActive,
object: nil)
// When Siri returns control to the app, a UIApplicationDidBecomeActive notification is fired
NotificationCenter.default.addObserver(
self,
selector: #selector(handleActive(_:)),
name: .UIApplicationDidBecomeActive,
object: nil)
}
After much searching, i've figured out the following:
On tvOS it doesn't seem like a Siri interruption fires a .AVAudioSessionInterruption notification, despite what the official documentation says. (outdated?, not applicable to tvOS?)
A Siri interruption is actually sending the app to the background, so it is possible to react by watching .UIApplicationWillResignActive and .UIApplicationDidBecomeActive instead.

Related

Native PWA getUserMedia permission prompt on iOS 1.16.2

In a PWA i require the users camera access for scanning QR codes. i use getUserMedia() to get the users permission, however, when you leave the device for a while and want to scan more QR codes, the devices prompts for permission again.
I can accept these permissions globally for the safari app so it doesn't show up when using the app in PWA mode.
However, using it as native PWA installed from the app-store, i get the prompt many times.
the app has a global setting for camera access, and it's set to true. html object <input type="file" accept="image/*" capture="environment"> then will NOT prompt the message again. however getUserMedia() WILL always keep prompting.
i find tons of peoples posts on the internet going back a couple of years.
I want the prompt to only show once.
solved:
#available(iOS 15.0, *)
func webView(_ webView: WKWebView,
requestMediaCapturePermissionFor
origin: WKSecurityOrigin,initiatedByFrame
frame: WKFrameInfo,type: WKMediaCaptureType,
decisionHandler: #escaping (WKPermissionDecision) -> Void){
decisionHandler(.grant)
}

How to pass/parse server data to objects in WatchKit table rows?

My tutorial is a WhatsApp/SnapChat app. Naturally the avatar image, country flag, user name, gender symbol and conversation data all come from the server and host app.
These kinds of apps do not use Parse like APIs or other 3rd party dependencies because they use REST/JSON with their own servers.
How do I get this same data and UI elements onto the watch table row? Do we have to re-write the same HTTP GET methods in our watch extension as well as re-copy UI elements into watchOS cassettes folder? Can we not just call the same methods that already exist in the iOS host app? I'm not sure how the Connectivity Framework would be used.
Could you please give an example of GET and POST methods to assign an avatar or username to the watch table row view object? For example for a Node.js server.
Since your iOS host app has already downloaded and deserialized the data, it doesn't make any sense for the watch to duplicate that code or effort and GET the same data.
As for providing an example, you should show what you tried in code, and explain the specific problem you're having.
Documentation
You should use the Watch Connectivity framework to share data between your iOS and watchOS apps.
You'll find a good introduction in the watchOS 2 Transition Guide. See Communicating with Your Companion iOS App for details.
Apple also provides Lister sample code which demonstrates how to use WCSession to transfer both application context and files between iOS and watchOS.
Since the host app is written in Obj-C should WatchConnectivity / WCSessionDelegate be imported into every file header file that contains data that needs to be sent to the watch extension?
WCSession is a singleton that you configure at launch time, early in the life of both your iOS app and watch extension. See the transition guide's Activating the Session Object for more information.
If you don't understand how or where your apps should handle watch connectivity, there are plenty of tutorials and sample projects which you can easily find via Google.
So based on what you said I just need to use the Connectivity Framework. sendMessageToWatch and didReceiveMessage methods.
The exact methods you use depend on what you want to transfer -- application context, user info, files, or messages -- and whether it takes place in the foreground or background. See the transition guide's Choosing the Right Communication Option for more information.
If you check some chat app projects that are already on GitHub you will see how to use Connectivity Framework precisely. Obj-C and Swift.
Here's one that specifically shows you how to pass messages back and forth.
https://github.com/carbamide/MessagingTest
This is not my code. As you can see code is almost the same.
ViewController.swift
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print(applicationContext)
let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
let alert = UIAlertController(title: "Application Context Received", message: applicationContext.description, preferredStyle: .Alert)
alert.addAction(okButton)
self.presentViewController(alert, animated: true, completion: nil)
}
watchOS InterfaceController.swift
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print(applicationContext)
let okButton = WKAlertAction(title: "OK", style: WKAlertActionStyle.Default, handler: { () -> Void in })
self.presentAlertControllerWithTitle("Application Context Received", message: applicationContext.description, preferredStyle: .Alert, actions: [okButton])
}

Wait for App to idle after Interruption

I have a ViewController that will request access to location services on init via
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)
{
[_locationManager requestWhenInUseAuthorization];
}
This triggers the "Allow app to access your location while you use the app?"-alert.
I use [self addUIInterruptionMonitorWithDescription:handler:] to react to this. I am encountering the following problem: after dismissing the request-dialog, the ui-test does not continue. The alert is dismissed, but Xcode waits for the app to become idle, but it looks like the app is idle:
t = 67.35s Wait for app to idle
The test fails, because the app is stuck here. If i tap into the simulator, Xcode logs.
t = 72.27s Synthesize event
and continues the test.
Is there a reason, why Xcode tries to wait for the app? A workaround seems to be to tell Xcode that the UI changed or an event happened. Is there a way to trigger this?
After presenting the alert you must interact with the interface. This is a known bug with Xcode 7.2. Simply tapping the app works just fine, but is required.
addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
app.buttons["Find Games Nearby?"].tap()
app.tap() // need to interact with the app for the handler to fire
XCTAssert(app.staticTexts["Authorized"].exists)
See my blog post for more information.

XCode 7 UI Testing: Dismissal of system-generated UIAlertController does not work

I have a UI test which involves the dismissal of a system-generated UIAlertController. This alert asks the user for the permission to access the device's calendar. The objective of the test is the behaviour after a tap on the OK button:
1 let app = XCUIApplication()
...
// this code was basically generated by the recording feature of XCode 7
2 app.alerts.elementBoundByIndex(0).collectionViews.buttons["OK"].tap()
Now, instead of clicking the OK button, line 2 makes the simulator tap onto the first button which happens to be the Cancel button...
Additionally, I found out that the testing framework does not accurately recognize the appearing alert. So if I check the current count of alerts I always get 0:
// ...tap...
let count = app.alerts.count // == 0
This also happens if I use an NSPredicate for the condition and wait for several seconds.
Is it possible that UI tests do not work reliably with system-generated alerts? I am using XCode 7.0.1.
Xcode 7.1 has finally fixed the issue with system alerts. There are, however, two small gotchas.
First, you need to set up a "UI Interuption Handler" before presenting the alert. This is our way of telling the framework how to handle an alert when it appears.
Second, after presenting the alert you must interact with the interface. Simply tapping the app works just fine, but is required.
addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
app.buttons["Request Location"].tap()
app.tap() // need to interact with the app for the handler to fire
The "Location Dialog" is just a string to help the developer identify which handler was accessed, it is not specific to the type of alert. I believe that returning true from the handler marks it as "complete", which means it won't be called again.
I managed to dismiss access prompt for contacts like this:
let alert = app.alerts["\u{201c}******\u{201d} Would Like to Access Your Contacts"].collectionViews.buttons["OK"]
if alert.exists
{
alert.tap()
}
Replace asterisks with your app's name. It might work the same for calendar.
This is what I ended up using to get the prompt to appear and then allow access to Contacts:
func allowAccessToContacts(textFieldsName: String)
{
let app = XCUIApplication()
let textField = app.textFields[textFieldsName]
textField.tap()
textField.typeText("aaa")
let alert = app.alerts["\u{201c}******\u{201d} Would Like to Access Your Contacts"].collectionViews.buttons["OK"]
if alert.exists
{
alert.tap()
}
textField.typeText("\u{8}\u{8}\u{8}")
}

Chromecast event for device disconnect (sender)

Is there event that will notify a sender app when the use selects "stop cast" from within the chrome extension?
I've a chrome sender app get's in a limbo state if the user chooses to stop the cast from the extension instead of the app cast button.
EDIT:
This is some relevant code:
CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) {
this.currentMediaSession = mediaSession;
// ...
this.currentMediaSession.addUpdateListener(this.onMediaStatusUpdate.bind(this));
// ...
};
CastPlayer.prototype.onMediaStatusUpdate = function (e) {
console.log(e);
};
Have you tried Session.addUpdateListener(listener) ? I think the listener will be notified when the session is no longer alive.
It seems Google developers are pretty aware of that! :D
They've just update their senders sample code with a commit that is exactly what seems you're looking for: Added session update listener to handle disconnect by clicking cast extension
There's also another commit with the same text in another sample but with less code, here you have: https://github.com/googlecast/CastHelloVideo-chrome/commit/776559c9aaf16d7d82c62ee4dea611b6177ac217