Wearable DataAPI syncing issue with multiple watches (Looping) - wear-os

I have a watchface with a companion app for phone. It uses Wearable.DataApi to sync changes between the phone and watch. I have a DataApi.DataListener setup and sync changes made on the watch or phone side. I have no issue with a phone and ONE watch communicating.
The issue is when i have multiple watches using the same watch face if changes on watch or phone side are made quickly it seems to go into a loop and start flashing the changes on all devices. So if im changing the color by tapping watch if I press a few times quickly to do that all devices start cycling through all colors and takes some time before it catches up and stops.
If I change options slowly there is no problem. I put a log in the DataApi listener and I see both uri's making the change but just seems to loop for some reason when changed quickly. Is there anyway to prevent this?
I know this might not seem like a big issue but if a user has 2 watches and accidently changes an option or options quickly it will start with the options and or colors changing. I want to prevent that from happening.
This is how im adding my listener in the onConnected method
Wearable.DataApi.addListener(mGoogleApiClient, dataListener);
And this is my listener method
DataApi.DataListener dataListener = new DataApi.DataListener() {
#Override
public void onDataChanged(DataEventBuffer dataEvents) {
Log.d(TAG, "onDataChanged");
for (DataEvent event : dataEvents) {
Log.d(TAG, "dataEvent.uri: " + event.getDataItem().getUri().toString());
DataMap item = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
/////other code to set data/////
}
updateCanvas();
}
};

You should be able to avoid this by filtering out dataEvents that originated from the local node (i.e., the device that the code is running on).
Get the local node ID with code like this:
NodeApi.GetLocalNodeResult nodeResult = Wearable.NodeApi.getLocalNode(googleApiClient).await();
String localNodeID = getLocalNodeResult.getNode().getId();
Then, put code like this inside your for loop to do the filtering:
Uri uri = item.getUri();
String nodeID = uri.getHost();
if (nodeID.equals(localNodeID)) {
// Skip changes originating on this device
continue;
}

Related

How do I restart a series of dialogs in bot framework?

I've got a simple bot game I'm developing. It has a Root Dialog, which can take you to two others - A Start Dialog and a Join Dialog.
Join Dialog can lead to the Play Game Dialog, which when the game is over leads you to a Score Dialog.
Feels like a lot bot (sic) but it runs smoothly. When scoring is done, I'd like to get back to the Root Dialog for another round.
But I'm stuck.
Even though I issue both an EndDialog() activity and a CodeAction() activity that simply calls DialogContext.CancelAllDialogs(), I remain within what looks like the EndDialog and therefore I don't get back to Root.
Thus, I can't restart my game.
Is there something I'm missing? I'm using both Adaptive Dialogs as well as Adaptive Cards and Hero Cards. Although, I don't think the cards should matter.
So, this is what I ended up doing to get what I want. It might not be the best way, but it works:
I created a CodeAction dialog that runs when the message I want to trigger a round restart to come in - there's a specific intent that is tied to that.
The CodeAction calls this method:
private async Task<DialogTurnResult> CancelAndReturnToRoot(DialogContext dc, object options)
{
var lastDialog = dc;
var secondToLastDialog = lastDialog;
var journeysUp = 1;
while (lastDialog != null)
{
lastDialog = lastDialog.Parent;
if (lastDialog != null)
secondToLastDialog = lastDialog;
journeysUp++;
}
return await secondToLastDialog.CancelAllDialogsAsync();
}
This seems to release the dialogs that I created and let me get back to the beginning dialog.

XamarinForms Shell OnResume - previous page is showing

I want to achieve lock mechanism in XF 4.5.0 Shell app.
I have static timer that counts how much seconds passed, and based on some logic present either screen that user was before put the app in behind (user went to instagram, but app is in the background running)
So, my OnResume() method looks like this:
private bool IsUserExists()
{
if (!string.IsNullOrWhiteSpace(GlobalAppSettings.Email) && !string.IsNullOrWhiteSpace(GlobalAppSettings.Password) && !string.IsNullOrWhiteSpace(GlobalAppSettings.Pin))
{
return true;
}
else
{
return false;
}
}
protected override void OnResume()
{
if ((!OnSleepOrPauseTime.HasValue || OnSleepOrPauseTime.Value.AddSeconds(5) <= DateTime.Now)
&&(IsUserExists()))
{
Xamarin.Forms.Device.BeginInvokeOnMainThread(async () =>
{
await Shell.Current.GoToAsync(Routes.LoginPinPage, false);
});
}
}
The problem: user sees for a like half second the screen where he was before, and then it redirects to LoginPinPage.
The happy path: Should be that user doesnt see that flickering page that was before exiting form an app, just immediately lock screen.
Possible solution: That crossed on my mind, before leaving the app to somehow redirect in background app to login pin screen?
PushAsync has 2 overloads - docs
By default the PushAsync method pushes the new page with animation. I think that this is the so-called "flickering" that you are referring to.
You can try to use the second overload and pass false for the animated property and see if it makes any difference.
PushAsync(new LoginPinPage(), false);
If you want to redirect the user on app leave - you can use another of the Application's lifecycle methods - OnSleep and invoke your navigation there. But I suggest to try it without animations first.
Keep in mind that, should you choose to use the OnSleep method, you will also have to push the page without animations. I have just tested pushing with animations and I do indeed see the flickering that you are mentioning. Setting the animated flag to false, removed any flickering for me.

Input.Touch not working on OSX

I'm a total beginner in Unity and created a function that simply sets the text of a text element when a touch is detected like this:
// Update is called once per frame
void Update () {
if (didTap ()) {
print ("did tap!");
tapText.text = "TAP!";
}
}
private bool didTap() {
print ("checking didtap");
if (Input.touchCount > 0) {
Touch initialTouch = Input.GetTouch (0);
return initialTouch.phase == TouchPhase.Began;
}
return false;
}
When I run this on an actual device (Android phone) it works perfectly, but when I 'click' in the unity editor when running the game nothing happens. I'm running OSX 10.12.2. Anything I need to configure to have the mouse mimic touch events?
You can duplicate Unity's play mode window onto your mobile device by using the UnityRemote app. This will allow you to interact with your phone's features, while still running Unity on your computer.
UnityRemote requires some setup in Unity along with downloading the app. Consider watching this video: Unity Remote Setup Tutorial
Alternatively, see the answer from Hellium in this question, in conjunction with getting the mouse clicks, to only compile code based on the device used.
There's an odd difference between "touch" input and "mouse" input in Unity. Input.touchCount will always return 0 while you're running in the Editor but Input.GetMouseButtonDown(0) works for both the Editor and the first touch of one or more touches while on a mobile build.
You have two options to solve this issue via code (your other option is the remote ryemoss mentioned):
If you're only ever using one touch, use Input.GetMouseButtonDown(0) and Input.GetMouseButtonUp(0).
Use #if UNITY_IOS, #if UNITY_ANDROID and #if UNITY_EDITOR tags to write separate input readers for your platforms and then have them all call into the same functionality.

GCD hangs in menu bar app

I have a menu bar app, which out of nowhere (no code changes) stopped working. Oddly the app continues to execute once I select it, or if I use any of the system keys (volume, brightness, iTunes, etc...). My code uses GCD to manage behavior. Is there a way to debug this and see what's going on behind the scenes? My confusion is why using the system keys (which pop up a window to show volume level, brightness level) makes my software continue execution?
DispatchQueue.main.async() { doSomething() }
func doSomething() {
//run a bunch of things in a background process
//so far works ok
let fooFrame = NSMakeRect(0, 0, 100, 100)
print("tag1") //this prints just fine
DispqtchQueue.main.sync() {
print("tag2") //this doesn't print and doesn't execute until I click on the app or use the volume keys.
//run code to update UI works fine once I use volume keys (no idea why)
self.window.frame = fooFrame
}
}
Also I am not getting any messages or exceptions raised. Just the app freezes. I have also noticed that if I have multiple displays and activate a second display, where the menu bar activates, then my process continues again and everything is normal.
Calling AnyQue.sync from the the same queue (AnyQueue) causes a deadlock, as the calling task is waiting for the called one. But the called one will start after the calling one finished.
As written by matt, using async fixes the issue. This code:
func doSomething() {
print("tag1")
DispatchQueue.main.async() {
print("tag2")
}
}
DispatchQueue.main.async() {
doSomething()
}
prints:
tag1
tag2
Figured it out, I had a CFRunLoop running which needed to be stopped and then everything seems to work fine now. Also additionally, the entire process was running as a background dispatch process, that's why using sync worked.

How can one detect Mission Control or Command-Tab switcher superseding one's program in OS X?

I'm trying to use CGAssociateMouseAndMouseCursorPosition(NO) in a program. This disconnects the mouse from the on screen cursor when your application is "in the foreground". Unfortunately it also disconnects it when Mission Control or the application switcher or who knows what else comes up.
So far I know:
The application is still active.
The window is still key.
Nothing is sent to the default notification center when these things come up.
The application stops receiving mouse moved events, but an NSEvent addGlobalMonitorForEventsMatchingMask:handler: also does not receive them, which is strange to say the least. It should receive any events not delivered to my application. (I was planning to detect the missing events to know when to associate the mouse again.
So, is there a way to detect when my application is no longer in control, specifically because Mission Control or the switch has taken over? They really expect the mouse to work and I need to restore that association for them.
I share your surprise that a global event monitor isn't seeing the events. In a similar situation, I used a Quartz Event Tap for a similar purpose. The Cocoa global event monitor is quite similar to event taps, so I figured it would work.
I put the tap on kCGAnnotatedSessionEventTap and compared the result from CGEventGetIntegerValueField(event, kCGEventTargetUnixProcessID) to getpid() to determine when the events were going to another app (e.g. Mission Control or Exposé). (I disable the tab when my app resigns active status, so it should only receive events destined for another app when this sort of overlay UI is presented.)
By the way, you mentioned monitoring the default notification center, but, if there's a notification about Mission Control or the like, it's more likely to come to the distributed notification center (NSDistributedNotificationCenter). So, it's worth checking that.
I needed to check for mission control being active and ended up with an approach along the lines of Ken's answer.
Sharing is caring so here is the smallest sensible complete code that worked for me: (Swift 5)
import Foundation
import AppKit
let dockPid = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.processIdentifier
var eventTargetPid: Int32?
let eventTap = CGEvent.tapCreate(
tap: .cgAnnotatedSessionEventTap,
place: .headInsertEventTap,
options: .listenOnly,
eventsOfInterest: CGEventMask(
(1 << CGEventType.mouseMoved.rawValue)
| (1 << CGEventType.keyDown.rawValue)
),
callback: { (tapProxy, type, event, _:UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? in
// Now, each time the mouse moves this var will receive the event's target pid
eventTargetPid = Int32(event.getIntegerValueField(.eventTargetUnixProcessID))
return nil
},
userInfo: nil
)!
// Add the event tap to our runloop
CFRunLoopAddSource(
CFRunLoopGetCurrent(),
CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0),
.commonModes
)
let periodSeconds = 1.0
// Add a timer for periodic checking
CFRunLoopAddTimer(CFRunLoopGetCurrent(), CFRunLoopTimerCreateWithHandler(
kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + periodSeconds, periodSeconds, 0, 0,
{ timer in
guard eventTargetPid != dockPid else {
print("Dock")
return
}
print("Not dock")
// Do things. This code will not run if the dock is getting events, which seems to always be the case if mission control or command switcher are active
}), .commonModes)
CFRunLoopRun()
This simply checks whether the dock was the one to receive the last event of interest (here that includes mouse movement and key-downs).
It covers most cases, but will report the wrong value between the command switcher or mission-control hiding and the first event being sent to a non-dock app. This is fine in my use-case but could be an issue for other ones.
Also, of course, when the dock at the bottom is active, this will detect that too.
Have you tried asking NSRunningApplication?

Resources