Is there any way to create different vibrations for different messages in wear os in code? - wear-os

I want to send different vibrations to the watch depending on what message I am sending. I am open to use notifications or messages, but I want to control the vibration.
This is for a program so the subject knows if there is a positive response (say 1 quick vibration) or a negative response (a long vibration or two quick vibrations).
Can this be done? I essentially would want two buttons in an app sending different notifications with different vibrations to the watch.

Yes it's possible. Just tag your message or notification with an ID indicating the type of vibration you want to trigger. On the watch make sure to check the ID and create the appropriate vibration pattern.
It might look something like this in your wearable code:
// Get the ID from the message/notification
// Create the vibration pattern
long[] pattern;
if(id == POSITIVE) {
pattern = new long[]{0, 200, 50, 200};
} else if(id == NEGATIVE) {
pattern = new long[]{0, 50};
}
// Trigger the vibration
((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(pattern, -1);
Remember to declare the vibrate permission in your manifest file:
<uses-permission android:name="android.permission.VIBRATE"/>

Related

Showing more than one line of the notification description in the notification tray using an extension

I am currently designing an extension to make the notifications in the notification section of the calendar expendable. The goal is to make the noficiation expand like the initial notification on the desktop does. I have changed the type of notification added to the noficiation tray to class NotificationBanner from class NotificationMessage. I am currently using a work-around to make this work, this is what my expand function looks like:
expand(animate) {
this.expanded = true;
this._actionBin.visible = this._actionBin.get_n_children() > 0;
if (this._bodyStack.get_n_children() < 2) {
this._expandedLabel = new MessageList.URLHighlighter(this._bodyText,
true, this._useBodyMarkup);
this.setExpandedBody(this._expandedLabel);
}
if (animate) {
if (!this.clickedByButton && !this.forceExpansion) {
// This is the usual way notifications are expanded, using the layout manager
this._bodyStack.ease_property('#layout.expansion', 1, {
progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
duration: MessageTray.ANIMATION_TIME,
});
}
else if (this.forceExpansion || this.clickedByButton) {
// When auto expanding or clicked by button, change height of body
oldHeight = this.bodyLabel.get_height();
const lines = Math.ceil(this._bodyText.length / 54);
this.bodyLabel.set_height(lines * this.bodyLabel.get_height());
}
this._actionBin.scale_y = 0;
this._actionBin.ease({
scale_y: 1,
duration: MessageTray.ANIMATION_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
} else {
this._bodyStack.layout_manager.expansion = 1;
this._actionBin.scale_y = 1;
}
this.emit('expanded');
}
As you can see, I have 2 options for this extension: Force expand all notifications or make the user use a button to expand. The current solution is not elegant, it simply changes the height of the notification label which manages the body. Furhermore, the notification body still shows the three dots, implying that the body is still not expanded. I believe this to be an issue with the layout manager, since the proper way to expand is to set message._bodyStack.layout_manager.expansion to 1. That does not work in the case of expanding a message in the notification tray. Is anyone familiar with the layout manager or can help me find a different solution? Here is an image of what my current solution looks like:
Image of an automatically expanded notification in the notification tray due to the extension (note the three dots at the end of the first line being still there)
Okay I have found a solution, it is not related to the layout manager. The value of the message message.bodyLabel.clutter_text.ellipsize is set to 3, which is the main cause of the dots appearing on the notification. Setting this value to 0 solves this problem. I would have still loved to find a more elegant approach to displaying the body, but this will do.

Microsoft Teams incoming call event

I would like to create a app which will be informed when a user gets a call in MS Teams. I mean I want to subscribe something on event of incoming call and then do something based on information of incoming call. Is this possible? So far I do not see any events in SDK.
There seems to now be a feature that may suit this.
Call records to provide usage and diagnostic information about the calls
and online meetings that occur within your organization when using
Microsoft Teams or Skype for Business.
...
Using webhooks and the MS Graph Subscription API, you can receive a continuous feed of call records as
they are created.
I investigated this issue for three days or so. These are my findings:
The MS Graph API is too slow. See https://learn.microsoft.com/en-us/graph/webhooks#latency. callRecord Notifications have a guaranteed latency of <60 minutes. Also, callRecords are only created after the call has finished, so they're useless for incoming calls.
I didn't want to write a MS Teams bot for this. I don't want my code to sit between each and every call just to get some information. Also, I think that bots would not work for calling a user's personal number, only for service accounts (Call queues / Auto Attendants).
The MS Teams client (I only checked on Windows) does not write the phone number into any file before the call is answered. By watching storage.json, you can figure out more or less reliable whether the phone is currently ringing, but without the calling number.
The indexedDB cache files eventually contain the calling number, but only once the call is answered. Also, I didn't find a library to read IndexedDB files that are on disk.
I did not find any third party software that could do this. Some paid Team apps can do this for calls to service accounts (e.g. Landis Contact Center)
The only thing I could come up with was to read the text out of the notification window itself. After a lot of trial, error and pain, I managed to get this:
//needs a COM reference to UIAutomationClient
//using UIAutomationClient;
[DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
internal static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
public void GetTeamsCallNotifications() {
do {
var teamsNotificationWindowHandle = FindWindowByCaption(IntPtr.Zero, "Microsoft Teams Notification");
try {
var pUIAutomation = new CUIAutomation();
var windowElement = pUIAutomation.ElementFromHandle(teamsNotificationWindowHandle);
var trueCond = pUIAutomation.CreateTrueCondition();
while (IsWindow(teamsNotificationWindowHandle)) {
//incoming call window has: two buttons (type 50000), a ISO phone number in a group-field (50026), and no text field (50020)
if (IsWindowVisible(teamsNotificationWindowHandle)) {
var elementArray = windowElement.FindAll(TreeScope.TreeScope_Descendants, trueCond);
string number = "";
int noButtonsFound = 0;
var debugFields = new List<string>();
for (int i = 0; i < elementArray.Length; i++)
{
var element = elementArray.GetElement(i);
debugFields.Add($"{element.CurrentControlType}={element.CurrentName}");
if (element.CurrentControlType == 50000)
noButtonsFound++;
if (element.CurrentControlType == 50026 && System.Text.RegularExpressions.Regex.IsMatch(element.CurrentName, #"^\+[1-9][0-9 ]+$"))
number = element.CurrentName.Replace(" ", "");
}
Debug.WriteLine(string.Join(";", debugFields) + "\r\n");
if (noButtonsFound == 2 && !string.IsNullOrEmpty(number))
Console.WriteLine(number + " is ringing");
}
Thread.Sleep(500);
}
}
catch { }
Thread.Sleep(5000); //Teams is probably closed, need a new window handle
} while (true);
}
Some comments:
The Teams Notification Window always exists when MS Teams runs, it's just either hidden or visible
There only ever exists one Teams Notification Window, even when multiple notifications are shown. windowElement.FindAll will give you all the elements of all notifications.
The code is pretty light-weight
Limitations of this code:
Windows only, not centralized (i.e. needs to run on every client)
A change in the layout of MS Teams could break it
It's unknown whether the call was answered or not.
A second notification of any kind will break it temporarily (until the second notification disappears).
You can improve on the last limitation if you're willing to accept other limitation. For instance, you can just search all text fields for a phone number. But then the code will trigger if someone sends you a text message containing a phone number. Or you can find the call notification pretty reliably if you know the display language of the Teams client by looking at the caption of the answer / decline buttons.

Google Cast custom receiver timing out

Using the Google CAF Receiver SDK, how do we prevent the receiver from timing out and automatically killing the cast session when we're not using the receiver player?
The standard Google Cast use case is to send media from a device to the cast receiver and have the receiver render the media using a player. The CAF receiver SDK provides this functionality in a beautiful, simple way using the element cast-media-player.
But for those instances when we want to cast from a device and render content where it's not relevant to use the cast-media-player (e.g. an HTML dashboard), how do we keep the receiver alive?
The following custom receiver for example (HAML for brevity), results in the cast session automatically terminating after 5 minutes...
!!! 5
%html
%head
:css
cast-media-player {
display: none;
}
= javascript_include_tag 'https://www.gstatic.com/cast/sdk/libs/caf_receiver/v3/cast_receiver_framework.js'
%body
%cast-media-player
:javascript
const context = cast.framework.CastReceiverContext.getInstance();
const player = context.getPlayerManager();
player.setMessageInterceptor(cast.framework.messages.MessageType.LOAD, loadRequestData => {
...[load custom view]...
return false;
});
context.start();
The receiver log shows the line cast.framework.common.IdleTimeoutManager] timer expired and then shuts down. Example receiver log shown here.
I've tried:
Increasing cast.framework.CastReceiverOptions#maxInactivity to a very large number
Periodically loading new data from the sender
Periodically sending custom messages from the receiver to the sender
Periodically sending custom messages from the sender to the receiver
Any help is very much appreciated!
I ran into the same problem while developing a custom receiver app that does not play media. Here is the solution I implemented:
var idleTime = 0;
const context = cast.framework.CastReceiverContext.getInstance();
const CUSTOM_CHANNEL = '[MY CHANNEL HERE]';
context.addCustomMessageListener(CUSTOM_CHANNEL, function(customEvent) {
var eventData = customEvent.data;
parseCommand(eventData);
idleTime = 0;
});
const options = new cast.framework.CastReceiverOptions();
options.disableIdleTimeout = true;
context.start(options);
var idleInterval = setInterval(timerIncrement, 60000); // 1 minute
function timerIncrement() {
idleTime = idleTime + 1;
if (idleTime > 4) { // 5 minutes
context.stop();
}
}
With CastReveiverOptions I disable idle timeout, which according to the documentation: "If true, the receiver will not set an idle timeout to close receiver if there is no activity. Should only be used for non media apps."
https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.CastReceiverOptions#constructor_1
Since mine is a "non media app," I believe this is correct usage.
I then set my own time out based on 5 minutes of inactivity in my custom channel.
I figured out an alternative way to stop this which is more efficient than periodically sending a silent clip, but it feels dirty. Basically we have to stop Chromecast's setTimeout from firing and closing the connection due to no media. The quickest solution is to simply re-declare setTimeout as a dummy no-op function before loading the Chromecast receiver script. It does not seem to break anything Chromecast-related in this scenario because it looks like Chromecast's timeouts are all related to video which aren't relevant to this use case.
window._setTimeout = window.setTimeout;
window.setTimeout = function(a, b) {
// disable setTimeout so chromecast won't kill us after 5 minutes...
};
Then in our own app if we need to use a timeout we call _setTimeout instead.
I would be interested if anyone has discovered a better way to achieve this, aside from manually hosting cast_receiver_framework.js with the offending line commented out (which is inside the Wn(a, b) function) or sending a silent clip every few minutes. But self-hosting isn't recommended by Google.
A better solution may be to dig deep in the minified code to work out how Xn(a) is called as that disables the timeout whenever media is playing, and then find a way to call that from within the Chromecast app.
Loading a short inaudible audio clip from the sender to the receiver every 4 minutes seems to do the trick. This should not impact performance much if the file is small. Here is some android code.
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
MediaInfo mediaInfo = new MediaInfo.Builder("https://some-inaudible-clip.mp3")
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("audio/mpeg")
.setMetadata(metadata)
.build();
RemoteMediaClient remoteMediaClient = castSession.getRemoteMediaClient();
remoteMediaClient.load(mediaInfo, true);
It is possible to send a custom namespace message from the receiver to the sender. That should keep the heartbeat live. However, your use case is not directly supported by the Cast SDK, so you would have to experiment on a solution.

How to get the battery level from Kontakt.io beacons using AltBeacon API

I need to get the battery level from the kontakt.io beacons. I have set the layout as below and the DataFields are empty when I read the beacons in RangingBeaconsInRegion.
I was expecting I could read the battery level from the last bit as described in the Kontakt.io documentation.
This is my current code:
private BeaconManager InitializeBeaconManager()
{
BeaconManager bm = BeaconManager.GetInstanceForApplication(Xamarin.Forms.Forms.Context);
var iBeaconParser = new BeaconParser();
iBeaconParser.SetBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25");
bm.BeaconParsers.Add(iBeaconParser);
_rangeNotifier.DidRangeBeaconsInRegionComplete += RangingBeaconsInRegion;
bm.Bind((IBeaconConsumer)Xamarin.Forms.Forms.Context);
return bm;
}
void RangingBeaconsInRegion(object sender, RangeEventArgs e)
{
if (e.Beacons.Count > 0)
{
var beacon = e.Beacons.FirstOrDefault();
var data = beacon.DataFields.FirstOrDefault();
// here DataFields is empty!
}
}
I am using Xamarin Forms and this is the code for the Android Version.
Is this possible? or do I need to use the Kontakt.io API?
UPDATE
I have removed all parsers before apply the new layout and I am able to read the dataFields. However, I am getting a value 8 which I have no idea what this value means.
I am not positive the syntax on Xamarin, but try removing all existing beacon parsers before adding your custom one. I suspect the built in iBeacon parser is still active And it is matching first.
The battery level in Kontakt.io beacons are part of their "scan response packet", not part of the iBeacon structure, you'll have to use CoreBluetooth to read Characteristic 1, Service 5.
A quick breakdown of how this works is also described here, and the recently launched Xamarin component uses the same CoreBluetooth approach.

Flixel Game Over Screen

I am new to game development but familiar with programming languages. I have started using Flixel and have a working Breakout game with score and lives.
I am just stuck on how I can create a new screen/game over screen if a player runs out of lives. I would like the process to be like following:
Check IF lives are equal to 0
Pause the game and display a new screen (probably transparent) that says 'Game Over'
When a user clicks or hits ENTER restart the level
Here is the function I currently have to update the lives:
private function loseLive(_ball:FlxObject, _bottomWall:FlxObject):void
{
// check for game over
if (lives_count == 0)
{
}
else
{
FlxG:lives_count -= 1;
lives.text = 'Lives: ' + lives_count.toString()
}
}
Here is my main game.as:
package
{
import org.flixel.*;
public class Game extends FlxGame
{
private const resolution:FlxPoint = new FlxPoint(640, 480);
private const zoom:uint = 2;
private const fps:uint = 60;
public function Game()
{
super(resolution.x / zoom, resolution.y / zoom, PlayState, zoom);
FlxG.flashFramerate = fps;
}
}
}
There are multiple ways to go about doing this...
You could use different FlxStates, like I described in the answer to your other post: Creating user UI using Flixel, although you'll have to get smart with passing the score or whatever around, or use a Registry-type setup
If you want it to actually work like you described above, with a transparent-overlay screen, you can try something like this (keep in mind, the exact details may differ for your project, I'm just trying to give you an idea):
First, make sure you have good logic for starting a level, lets say it's a function called StartLevel.
You'll want to define a flag - just a Boolean - that tracks whether or not the game is still going on or not: private var _isGameOver:Boolean; At the very end of StartLevel(), set this to false.
In your create() function for your PlayState, build a new FlxGroup which has all the things you want on your Game Over screen - some text, an image, and something that says "Press ENTER to Restart" (or whatever). Then set it to visible = false. The code for that might look something like:
grpGameOver = new FlxGroup();
grpGameOver.add(new FlxSprite(10,10).makeGraphic(FlxG.Width-20,FlxG.Height-20,0x66000000)); // just a semi-transparent black box to cover your game screen.
grpGameOver.add(new FlxText(...)); // whatever you want to add to the group...
grpGameOver.visible = false;
add(grpGameOver); // add the group to your State.
Depending on how your game is setup, you may also want to set the objects in your group's scrollFactor to 0 - if your game screen scrolls at all:
grpGameOver.setAll("scrollFactor", new FlxPoint(0,0));
In your update() function, you'll need to split it into 2 parts: one for when the game is over, and one for if the game is still going on:
if (_isGameOver)
{
if (FlxG.keys.justReleased("ENTER"))
{
grpGameOver.visible = false;
StartLevel();
}
}
else
{
... the rest of your game logic that you already have ...
}
super.update();
Keep in mind, if you have things that respond to user input anywhere else - like a player object or something, you might need to change their update() functions to check for that flag as well.
Then, the last thing you need to do is in your loseLive() logic:
if (lives_count == 0)
{
_isGameOver = true;
grpGameOver.visible = true;
}
else
...
That should do it!
I would highly recommend spending some time with different tutorials and sample projects to kind of get a better feel for Flixel in general. Photon Storm has some great material to play with (even though he's jumped over to HTML5 games)
I also want to note that if you get comfortable with the way Flixel handles updates, you can get really smart with your state's update() function and have it only call update on the grpGameOver objects, instead of having to change all your other objects updates individually. Pretty advanced stuff, but can be worth it to learn it.

Resources