I made a program that handles NFC tags back in 2013, today after two years of pause on that program i continued with it, and ran into some strange problem. In the program i read NFC tags which are writen with a "window.Someid" so they are app specific.
For some reason, the program works perfectly when scanning the NFC tag, except, when i want to scan the tag for the second time, the handler doesn't fire. any idea what causes this? (i see no error message).
Here's my code:
ProximityDevice PDevice;
private long SubscriptionID = -1;
// Constructor
public MainPage()
{
InitializeComponent();
PDevice = ProximityDevice.GetDefault();
// subscribe for messages
SubscriptionID = PDevice.SubscribeForMessage("Windows.SomeMsg", ProximityMessageRcvd);
}
private void ProximityMessageRcvd(ProximityDevice sender, ProximityMessage message)
{
Debug.WriteLine("Message");
}
Related
When you close the app by swiping it in recent apps, it will cancel any services and terminate most aspects of the app gracefully. However, if there are any notifications that were SetOngoing(true), then these will remain if the app suddenly is closed, and there aren't any services that listen for the app's termination.
What is the right way to deal with this problem?
Recently, I coded a music player, and I arranged it such that in the OnStop for my activities, the notification is canceled (and so is the thread updating the progress bar within it). Then, OnResume, I trigger the notification again.
If they "recent apps swipe" it away, or click away, the notification goes away now, as long as the music isn't playing. So to get rid of the notification, you have to pause it, and then swipe away. Otherwise, there is a leak memory if the app is closed by swipe, where the notification remains open and is buggy afterwards if the app is reopened, and the app crashes if you click the notification (though maybe that's because I can't figure out how to get started with saved state bundles). Likewise, there is a problem if you let the app close the notification every OnStop, as then it will be closed as the user does other things with their phone, even though the music is playing (which sort of defeats the point of it right?)
Are there other better ways to handle this? Who has a good saved state bundle if that is indeed relevant to my issue?
Thanks for the discussion
You can cancel the notification when android App is closed by swipe with the following code:
[Service]
public class ForegroundServiceDemo : Service
{
public override void OnTaskRemoved(Intent rootIntent)
{
//this.StopSelf();
//this.StopForeground(StopForegroundFlags.Remove);
this.StopService(new Intent(this,typeof(ForegroundServiceDemo)));
base.OnTaskRemoved(rootIntent);
}
}
By overriding the OnTaskRemoved method of the service, the system will call this method when user closes the app by swipe. And each of the three lines code can cancel the notification and stop the service when the app is closed by swipe.
I found this, finally, after trying every search terms imaginable, and wow there is a whole section on this. I do not have it working yet, but I can report back with code when I do. Here is the solution: https://developer.android.com/guide/topics/media-apps/media-apps-overview
Seems you have to implement the media player service as a specific kind of service that registers to the notification. I am in the process of refactoring the heart of my code, which perhaps should be terrifying, but feels more like the final algorithm on a Rubix's cube... I will report back in like 10 work hours with some working code (I hope).
Thanks to everyone contributing on this discussion!
OK, so, after much dabbling and dozens of work hours... I have found the best way to handle this issue is to create a MediaBrowserService with a MediaSession. In the notification creation code, it is very particular about how you start that notification (which has to be in the Foreground and bound to the MediaSession). Once this is done, the notification will stay open, even if you close the app, and clicking it will always bring you back to the activity bound to the service (see the supplied code below). Then, you just have a button on the notification to close itself and the app. Voila, a notification that does NOT remain open if the app is closed from the recent apps, etc.
public static void CancelNotificationBreadCrumb()
{
if (cts != null)
{
cts.Cancel();
Thread.Sleep(250);
// Cancellation should have happened, so call Dispose
cts.Dispose();
MyLogger.Debug("MyMediaPlayer: CloseEntireApp: Notification should have been disposed.");
}
}
public static void NotificationNowPlayingBreadCrumb()
{
try
{
Intent intent = MenuManager.GetGoToNowPlayingIntent(context, GetCurrentlyPlaying());
manager = (NotificationManager)context.GetSystemService(NotificationService);
PendingIntent pendingIntent = PendingIntent.GetActivity(context, 1, intent, PendingIntentFlags.Immutable);
NotificationChannel notificationChannel = new NotificationChannel(ChannelId, ChannelId, NotificationImportance.Low);
notificationChannel.EnableLights(false);
notificationChannel.EnableVibration(false);
notificationChannel.SetSound(null, null);
//notificationChannel.SetVibrationPattern(new long[] { 10, 20 });
manager.CreateNotificationChannel(notificationChannel);
Notification notification = NowPlayingAdapter.InflateNotification(context, currentFile, ChannelId, pendingIntent);
service.StartForeground(MY_MEDIA_NOTIFICATION_ID, notification);
manager.Notify(MY_MEDIA_NOTIFICATION_ID, notification);
// Then trigger the thread to update the real-time features
if (cts == null || cts.IsCancellationRequested)
cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(RunInBackground), cts.Token);
} catch(Exception e)
{
string message = "MyMediaPlayer: NotificationNowPlayingBreadCrumb: Could not create now playing breadcrumb notification; message: " + e.Message;
MyLogger.Error(message);
}
}
public static void CloseEntireApp()
{
MyLogger.Trace("MyMediaPlayer: Entering CloseEntireApp...");
if (player != null)
player.Release();
CancelNotificationBreadCrumb();
MediaReceiver.Dispose();
MediaSession.Dispose();
MyLogger.Trace("MyMediaPlayer: CloseEntireApp is Killing App. Good bye!");
service.StopSelf();
Android.OS.Process.KillProcess(Android.OS.Process.MyPid());
}
Here is the OnCreate method for my service:
public class MyMediaPlayer : MediaBrowserServiceCompat
{
private static MediaPlayer? player;
private static MusicAppFile? currentFile;
private static List<MusicAppFile>? allFilesInCurrentContext;
private static Context? context;
private static List<int> recentIndexes = new List<int>();
private static int maxRecentIndexes = 30;
private static bool shuffleMode = false;
private static ViewGroup? Parent;
private static NotificationManager? manager;
private static CancellationTokenSource? cts;
public static MediaButtonReceiver? MediaReceiver;
public static MediaSessionCompat? MediaSession;
private static PlaybackStateCompat.Builder stateBuilder;
private static MediaBrowserServiceCompat service;
public IBinder Binder { get; private set; }
public const string ActionPlay = "com.xamarin.action.PLAY";
public const string ActionPause = "com.xamarin.action.PAUSE";
public const string ActionNext = "com.xamarin.action.NEXT";
public const string ActionStop = "com.xamarin.action.STOP";
public const string ActionBack = "com.xamarin.action.BACK";
public const string ActionCloseApp = "com.xamarin.action.CLOSEAPP";
public static string ChannelId = "NowPlayingNote";
public static string MY_MEDIA_ROOT_ID = "media_root_id";
public static int MY_MEDIA_NOTIFICATION_ID = 1111111;
public static string MY_MEDIA_TAG = "media_tag";
public override void OnCreate()
{
base.OnCreate();
// Create a MediaSessionCompat
MediaSession = new MediaSessionCompat(context, MY_MEDIA_TAG);
// Enable callbacks from MediaButtons and TransportControls
MediaSession.SetFlags(
MediaSessionCompat.FlagHandlesMediaButtons |
MediaSessionCompat.FlagHandlesTransportControls);
// Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
stateBuilder = new PlaybackStateCompat.Builder()
.SetActions(
PlaybackStateCompat.ActionPlay |
PlaybackStateCompat.ActionPlayPause |
PlaybackStateCompat.ActionSkipToNext |
PlaybackStateCompat.ActionSkipToPrevious |
PlaybackStateCompat.ActionStop);
MediaSession.SetPlaybackState(stateBuilder.Build());
// MySessionCallback() don't do this. C# isn't as good at doing callbacks because you can't define them inline
// MediaSession.SetCallback(new MediaSessionCallback(this));
service = this;
// Set the session's token so that client activities can communicate with it.
SessionToken = MediaSession.SessionToken;
}
...
I create this service when they click to select a file in one of the menu activities (so in a method called by a method called by an OnClick delegate):
if (musicMenu != null)
{
bool stillPlayingSameFile = MyMediaPlayer.UpdateCurrentContext(c, musicMenu, mf);
if (cts == null)
{
// Start the service and tell it to call play
InitiateMediaBrowserService(c);
} else
{
MyMediaPlayer.Play(stillPlayingSameFile);
}
}
GoToNowPlaying(c, mf);
and the inner service there:
public static void InitiateMediaBrowserService(Context c)
{
// Start the service and tell it to call play
Intent intent = new Intent(c, typeof(MyMediaPlayer));
intent.SetAction(MyMediaPlayer.ActionPlay);
cts = new CancellationTokenSource();
Platform.AppContext.StartForegroundService(intent);
}
Ok, so now the play service, which is triggered from the action play intent here, and makes the call to start the notification, which is where the StartForeground call is made (see the first snippet at the top):
public static void Play(bool stillPlayingSameFile)
{
// If the player has not been created before, or it is a new track, then it needs to be recreated
if (player == null || !stillPlayingSameFile)
{
// If we're here to recreate the player, destroy the old one in memory first
if (player != null)
player.Release();
// Then add the new player
if (currentFile != null)
{
Uri uri = Android.Net.Uri.Parse(currentFile.FilePath);
MediaPlayer media = MediaPlayer.Create(context, uri);
media.Completion += OnCompletion;
if (MediaReceiver == null)
MediaReceiver = new MediaButtonReceiver(context);
media.RoutingChanged += MediaReceiver.OnRoutingChanged;
player = media;
player.SetWakeMode(context, WakeLockFlags.Partial);
}
// Finally, add this file to the list of those recently played
int indexToPlay = allFilesInCurrentContext.IndexOf(currentFile);
if (indexToPlay >= 0)
recentIndexes.Add(indexToPlay);
if (recentIndexes.Count > maxRecentIndexes)
recentIndexes.RemoveAt(0);
}
// Finally start the player, which picks up where left off if this is the same track
if (!IsPlaying() || !stillPlayingSameFile)
{
player.Start();
NotificationNowPlayingBreadCrumb();
}
}
The MediaButtonReceiver and MediaBroadcastReceiver classes are pretty straightforward, so comment if you really need that code. One other thing to note is that you do have to bind the service to an activity (I suggest the now playing activity):
protected override void OnStart()
{
base.OnStart();
//Config.ConfigureBluetoothIntegration(this); TODO remove this
Intent serviceToStart = new Intent(this, typeof(MyMediaPlayer));
//serviceToStart.SetAction(MyMediaPlayer.ActionPlay);
BindService(serviceToStart, new ServiceConnection(this), Bind.AutoCreate);
}
So there, now there IS an example of how to use the MediaSession and MediaSessionCompat and MediaBrowserServiceCompat online somewhere. Even ChatGPT could not find an example or tell me how to do this. You are welcome, internet. Enjoy your coding!
I'm looking into DNS based service discovery in Windows 10 and found this video from Build. The only change I made to discovery was changing the service name to "_ipp._tcp". However I don't get any hits even though I know I have > 15 IPP enabled printers on the network (I can successfully identify these using the ipptool, IOS and Android code).
I've checked and double checked for typos. I've included all networking capabilities in the appxmanifest file.
Here's my code, pretty straight forward:
public sealed partial class MainPage : Page
{
static Guid DnsSdProtocol = new Guid("{4526e8c1-8aac-4153-9b16-55e86ada0e54}");
string queryString = "System.Devices.AepService.ProtocolId:={" + DnsSdProtocol + "} AND " +
"System.Devices.Dnssd.Domain:=\"local\" AND System.Devices.Dnssd.ServiceName:=\"_ipp._tcp\"";
DeviceWatcher watcher;
public MainPage()
{
this.InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
watcher = DeviceInformation.CreateWatcher(queryString,
new String[]
{
"System.Devices.Dnssd.HostName",
"System.Devices.Dnssd.ServiceName",
"System.Devices.Dnssd.TextAttributes",
"System.Devices.IpAddress" },
DeviceInformationKind.AssociationEndpointService
);
watcher.Added += Watcher_Added;
watcher.Start();
}
private void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
System.Diagnostics.Debug.WriteLine("found device");
}
}
Does anybody else have experience with this and can help figure out why no devices are found? Is my query correct? Do I need to do anything else when setting up the DeviceWatcher?
update
I've verified the requests are being created since they are showing up in Wireshark. They look to be identical to other mdns requests being created. I've also verified I can create SSDP requests that return discovered devices so I doubt it's an issue with networking permissions via app capabilities.
Try using the watcher.Updated handler. Even if it is empty, the presence of an Updated handler can cause the Added handler to be triggered.
I have an API (dll) that collects stock ticks via an event mechanism. Such as below:
...
using MT4API;
public partial class Blue : Form
{
...
public Blue()
{
...
string symbol = "GBPUSD";
MT4DDE dde = new MT4DDE("");
dde.OnQuote += new System.EventHandler<QuoteEventArgs>(MT_OnQuote);
dde.Connect();
dde.Subscribe(symbol);
....
The idea is that on each chart tick I get an event. here is the event handler code:
private static void MT_OnQuote(object sender, QuoteEventArgs args)
{
GlobalClass.Ask = args.Ask;
GlobalClass.Bid = args.Bid;
// I have back ground worker code that updatestables from the global class
}
This all works fine. So long as I do not touch any other buttons on the form UI. As soon as I click a button on the form of the UI... I no longer receive events from my API, the UI application functions normally, but with no data from the API.
Why do events from the UI stop any further events coming from the API event?
Any idea whats going on here? Or suggestions how to design this?
Does the same problem occur if you comment out your code that updates the tables from the global object? and if you comment out the background worker?
It would be a good idea to distinguish if the event stops being fired just after you press some button on the UI, or if it stops being fired only after some line of code you wrote is being executed.
In order to be able to help you, we would need to know how the event on the MT4DDE class is triggered.
If you have the code for this class, posting it would help.
If you don't you may want to use a tool such as Reflector to decompile the assembly into C# and see what the MT4DDE class is doing that might cause it to stop invoking the event.
In addition, if you are doing anything related to background threads, or if you're doing anything unusual with your application's main message loop, it would be a good idea to mention it here.
I have tried to use the invoke command, it works, but after a few events it stops...here is the code isolated:
using MT4API;
namespace WindowsFormsApplication1
{
public delegate void UpdateTextCallback(double ask, double bid);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string symbol = "GBPUSD";
MT4DDE dde = new MT4DDE("");
dde.OnQuote += new EventHandler<QuoteEventArgs>(MT_OnQuote);
dde.Connect();
dde.Subscribe(symbol);
}
private void updateTickDisplay(double ask, double bid)
{
textBox1.Text = ask.ToString();
textBox2.Text = bid.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void MT_OnQuote(object sender, QuoteEventArgs args)
{
BeginInvoke(new UpdateTextCallback(this.updateTickDisplay),
new object[] { args.Ask, args.Bid });
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox3.Text = textBox1.Text;
}
}
}
The only difference from the real code is that I am using a data grid....as opposed to a text field. But it is clear that the UI blocks somehow the new events. It is strange that I get about 5 to 10 events and then it just stops. Strange. Any ideas on a differnet design?
I've been working with a bit of speech recognition for a few days with various test programs and it's all worked fine. However i've tried implementing it into my OpenGL project and the function 'Recognized' is now not being called.
Up in the Windows Speech Recognition thing (the thing that says "try saying 'Start Listening'" an awful lot), words that are loaded appear when i say them, so I am assuming that it is correctly detecting words, it's just for some reason not triggering the event.
Here's the code i've been using. All you really need to know (besides what is shown in the code), is that AddCommands is called somewhere else, to add in a few words that i've been testing with and that 'Initiate' is called upon the loading of the form.
public class SpeechControls
{
public static SpeechRecognizer sRecognizer;
private static Dictionary<string, IVoiceControlable> controllers = new Dictionary<string, IVoiceControlable>();
public static void Initiate()
{
sRecognizer = new SpeechRecognizer();
sRecognizer.Enabled = true;
sRecognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(Recognized);
}
private static void Recognized(object obj, SpeechRecognizedEventArgs args)
{
controllers[args.Result.Text].TriggerCommand(args.Result.Text);
}
public static void AddCommands(string[] commands, IVoiceControlable control)
{
foreach (string str in commands)
{
controllers.Add(str, control);
}
sRecognizer.LoadGrammar(new Grammar(new GrammarBuilder(new Choices(commands))));
}
}
Does anyone know why 'Recognized' would not be triggered?
Thanks for any help, much appreciated.
Because OpenGL runs of game loops rather than event listening, the thread is completely taken up by the loop. To start listening for commands a second thread is needed.
I have a program in c# which uses my local webcam, to capture and store images. I have buttons to click on start,stop,continue n etc.When I run the program it works properly for the first time after I turn my system on, but in the consecutive executions of the same thing I get an error (in pop-up window):
An error occurred while capturing the video image. The video capture will now be terminated. Object reference not set to an instance of the object.
for which I assume that it might be because of the camera device, not releasing the memory it used. So how do I programatically free it up, when I click on the exit button? Below is part of the program and i get error in the webcam.start(0) method
namespace WinFormCharpWebCam
{
class WebCam
{
private WebCamCapture webcam;
private System.Windows.Forms.PictureBox _FrameImage;
private int FrameNumber = 30;
public void InitializeWebCam(ref System.Windows.Forms.PictureBox ImageControl)
{
webcam = new WebCamCapture();
webcam.FrameNumber = ((ulong)(0ul));
webcam.TimeToCapture_milliseconds = FrameNumber;
webcam.ImageCaptured += new WebCamCapture.WebCamEventHandler(webcam_ImageCaptured);
_FrameImage = ImageControl;
}
void webcam_ImageCaptured(object source, WebcamEventArgs e)
{
_FrameImage.Image = e.WebCamImage;
}
public void Start()
{
webcam.TimeToCapture_milliseconds = FrameNumber;
webcam.Start(0); //error pops up when the execution comes to this method
}
public void Stop()
{
webcam.Stop();
}
public void Continue()
{
// change the capture time frame
webcam.TimeToCapture_milliseconds = FrameNumber;
// resume the video capture from the stop
webcam.Start(this.webcam.FrameNumber);
}
public void ResolutionSetting()
{
webcam.Config();
}
public void AdvanceSetting()
{
webcam.Config2();
}
}
}
You have a NullReferenceException thrown, not OutOfMemoryException.
Check your call stack to pinpoint where it originates. You can debug your app with debugger set to break on exception thrown, so it will break right where your exception occurs (press CRTL+D, E to open exceptions window in VS.NET).
From the error you are getting, I am guessing you downloaded the EasyWebCam Library.
If that is correct then here's how I worked around it:
1. I installed the Cyberlink's Youcam software on my machine.
2. Whenever I start my own application, the EasyWebCam library detects the Youcam WebSplitter on machine and prompts me to select either that or the default webcam driver.
3. I select the YouCam WebSplitter and the app works fine with it.
At this point, there comes along another snag: the Youcam process doesn't terminate when my application closes.
How I fixed it?
I had to get the Youcam process and Kill() it when my application window's about to exit.
This ugly solution worked.