I've implemented this class of Microsoft.Windows.SDK.Contracts nuget package for monitoring the actions (play and pause) of a third party music player.
Everything works except that after some time the events are no longer triggered.
This is the code:
public void startMediaListener()
{
GlobalSystemMediaTransportControlsSessionManager.RequestAsync().GetAwaiter().GetResult().CurrentSessionChanged += callbackSessionChanged;
GlobalSystemMediaTransportControlsSession currentSession = GlobalSystemMediaTransportControlsSessionManager.RequestAsync().GetAwaiter().GetResult().GetCurrentSession();
if (currentSession == null || !currentSession.SourceAppUserModelId.ToLower().Contains("appNameToMonitor"))
return;
Console.WriteLine("Started");
currentSession.PlaybackInfoChanged += callbackPlaybackStatus;
currentHashSession = currentSession.GetHashCode();
serviceRunning = true;
}
Event when session changed
private void callbackSessionChanged(GlobalSystemMediaTransportControlsSessionManager session, CurrentSessionChangedEventArgs e)
{
Console.WriteLine("Session changed" + session.GetCurrentSession().GetHashCode().ToString());
if (session.GetCurrentSession() == null || !session.GetCurrentSession().SourceAppUserModelId.ToLower().Contains("appNameToMonitor"))
return;
session.GetCurrentSession().PlaybackInfoChanged += callbackPlaybackStatus;
currentHashSession = session.GetCurrentSession().GetHashCode();
}
Event for detecting pause/play
private void callbackPlaybackStatus(GlobalSystemMediaTransportControlsSession session, PlaybackInfoChangedEventArgs e)
{
if (!session.SourceAppUserModelId.ToLower().Contains("spotify") || currentHashSession != session.GetHashCode())
return;
GlobalSystemMediaTransportControlsSessionPlaybackStatus status = session.GetPlaybackInfo().PlaybackStatus;
double currentTime = session.GetTimelineProperties().LastUpdatedTime.TimeOfDay.TotalMilliseconds;
//Do work...
}
I also check the hashcode because if I listen to Spotify then youtube then Spotify again the 2 sessions of Spotify have different hashcode and it will trigger the event twice (when it works) and I want to monitor only the last one.
The problem is that in startMediaListener() I can find the session of my app and everything works for few minutes but after some time the callbackPlaybackStatus is no longer called and meanwhile no session changed so I suppose that the session is still up but not firing the event.
Can you help me solve this mistery?
Ps: I've tried to put everything inside a thread but this solution doesn't works.
Related
I am working on a barcode reader project in Visual Studio using C#. I have created a WinForm Application and have added a RichTextBox to it. I want the user to be able to start scanning when they open the program without having to click on the textbox.
Thanks in advance!
(I'm assuming you have an application with a multitude of stuff in it. However there is one field that needs to be filled in with a scanned barcode.)
I faced a simular issue a while ago. I needed to capture a barcode in WPF. Setting the focus property in load seemed a good idea but because there were a multitude of other controls on the page that the user could click etc. focus jumped from one control to the other, making the barcode go in the wrong fields or vanish in a grid that has focus for example.
We were not able to use any other way of reading the barcode from the scanner because it was used for other applications too. It had to be configured as input.
We came up with a solution of capturing the keypresses instead.
By using the keydown events we could track the scanner input and stated that if more than 5 keys came in within a limited time + with our prefix and suffix it had to be a barcode.
EDIT: here is a simplified version of the class.
public delegate void BarcodeRead(string barcode);
public class ManualReader
{
private string barcode = "no barcode detected";
private string possible = "";
private DateTime timestarted = DateTime.MinValue;
private Timer InputTimeout;
public BarcodeRead OnBarcodeRead;
public void OnKeyDown(object sender, KeyEventArgs.KeyEventArgs e)
{
//create timer if it does not exist
if (InputTimeout == null)
{
InputTimeout = new Timer(100);
InputTimeout.Enabled = true;
InputTimeout.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
//reset timer
InputTimeout.Stop();
InputTimeout.Start();
//possible barcode
possible += CharToKey.GetCharFromKey(e);
if (timestarted == DateTime.MinValue)
{
timestarted = DateTime.Now;
}
}
//Timer elapses
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
//Is it a barcode?
if ((timestarted.AddMilliseconds(600) > DateTime.Now) && (possible.Length > 5)
&& (timestarted != DateTime.MinValue) && possible.Contains("\r"))
{
barcode = possible;
barcode = barcode.Remove(0, 1);
barcode = barcode.Replace("\r", "");
//launch delegate
if (OnBarcodeRead != null)
{
OnBarcodeRead.Invoke(barcode);
}
}
//delete timers
timestarted = DateTime.MinValue;
InputTimeout.Dispose();
InputTimeout = null;
possible = null;
}
}
}
I'm aware that for really short timeouts datetime functions aren't precise but still this little 'hack' worked perfectly for our application.
You can add directly in the element. This works for textbox but not sure with RichTexBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
In my App you can open a Site where you can switch on and off the Flashlight.
The first time it works, but if I try to switch the flashlight on a second time the App crashes.
I think this is a Problem with AudioVideoCaptureDevice.OpenAsync. If I call it a second time the App crashes with a System.Reflection.TargetInvocationException WinRT-Informationen: Unable to acquire the camera. You can only use this class while in the foreground.
Someone know this Problem?
protected AudioVideoCaptureDevice Device { get; set; }
public Page10()
{
InitializeComponent();
}
async void tglSwitch_Checked(object sender, RoutedEventArgs e)
{
var sensorLocation = CameraSensorLocation.Back;
if (this.Device == null)
{
// get the AudioVideoCaptureDevice
this.Device = await AudioVideoCaptureDevice.OpenAsync(sensorLocation,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(sensorLocation).First());
}
var supportedCameraModes = AudioVideoCaptureDevice
.GetSupportedPropertyValues(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchMode);
if (supportedCameraModes.ToList().Contains((UInt32)VideoTorchMode.On))
{
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchMode, VideoTorchMode.On);
// set flash power to maxinum
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchPower,
AudioVideoCaptureDevice.GetSupportedPropertyRange(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchPower).Max);
this.tglSwitch.Content = "Light on";
this.tglSwitch.SwitchForeground = new SolidColorBrush(Colors.Green);
}
}
void tglSwitch_Unchecked(object sender, RoutedEventArgs e)
{
var sensorLocation = CameraSensorLocation.Back;
sensorLocation = CameraSensorLocation.Back;
var supportedCameraModes = AudioVideoCaptureDevice
.GetSupportedPropertyValues(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchMode);
if (this.Device != null && supportedCameraModes.ToList().Contains((UInt32)VideoTorchMode.Off))
{
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchMode, VideoTorchMode.Off);
this.tglSwitch.Content = "Light off";
}
}
I would recommend to initialize the camera with OpenAsync ONE TIME in page lifecycle, for example in OnNavigatedTo event. And only makeSetProperty() methods calls code in your checkbox events to control light. It is also very important to dispose camera correctly then leaving the page, for example in OnNavigatedFrom event, by calling device.Dispose(). This option also make your flashlight to work faster.
Keep in mind that Windows Phone 8.1 now has dedicated API for torch, which works great and the code is more beautiful. You can use in Silverlight project as well, but you have to migrate your project. Here is more about this http://developer.nokia.com/community/wiki/Using_the_camera_light_in_Windows_Phone_7,_8_and_8.1 and https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.torchcontrol.
I am developing one app in windows phone. In my app it is necessary to handle lock key press event because I have used Timer. Here is my code
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
PhoneApplicationService.Current.Activated += Micropphone_Current_Activated;
PhoneApplicationService.Current.Deactivated += Micropphone_Current_Deactivated;
if (_autowizardtimer != null)
{
_autowizardtimer.Stop();
_autowizardtimer.Tick -= _timer_Tick;
if (_endtimer != null)
{
_endtimer.Stop();
_endtimer.Tick -= _endtimer_Tick;
}
}
}
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
if (_autowizardtimer != null)
{
PhoneApplicationService.Current.Activated -= Micropphone_Current_Activated;
PhoneApplicationService.Current.Deactivated -= Micropphone_Current_Deactivated;
_autowizardtimer.Stop();
_autowizardtimer.Tick -= _timer_Tick;
}
if (_endtimer != null)
{
_endtimer.Stop();
_endtimer.Tick -= _endtimer_Tick;
}
}
Basically what I am trying to do is, when user press lock key the timer get's pause and after releasing lock key timer get's start. The problem is in OnNavigatedFrom method. When I press lock key this method is not calling. I am not getting what is the problem. Can some please tell why this is happening? Or Is there any other way to handle lock key press ?
You don't need to stop the timer when the screen is locked, because your application will automatically be deactivated (unless you've explicitly told the system that you want your application to run under the lock screen).
Still, if you want to detect the moment when the screen is locked or unlocked, you can use the events Obscured and Unobscured respectively.
http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206951(v=vs.105).aspx#BKMK_Understandingtheeventsthatoccurwhenthephoneislockedorunlocked
When you press the lock key you basically don't navigate from the page, but you're deactivating your app. In your case I would handle this timer in the App.xaml.cs Activated and Deactivated events also.
Please take a look at the Windows Phone Application Lifecycle here: http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff817008(v=vs.105).aspx
Regards,
I have a sub which starts one of two timers (depending on 'zone' condition). This sub called 'CheckAndActivateRelays' is itself called by a Serial Port _DataReceived event. I am inserting break points to help me troubleshoot and am seeing that the tmrSoundSirensAfterDelay.Start() line is being executed successfully with the status of the timer even changing to enabled. However the associated Tick event never executes any of the code contained within it.
If I do the same thing by calling the sub from within button24's click event, it works perfectly. Everything is on the same Form with no threaded processes.
Anyone? Thanks
private void checkAndActivateRelays(int zoneNumber)
{
if (globalFullAlarmSet || globalNightAlarmSet || globalDoorsAlarmSet)
{
if (zoneNumber == 1) //Entry zone
{
//kick off a timer after delay specified in Settings1 file,
if (Settings1.Default.alarmSirenDurationInMinutes != 0)
{
//activates the relays if global alarm flags are still set to true
//(i.e. user has not entered code in time)
globalAlarmEntryDurationTicks = 0;
tmrSoundSirensAfterDelay.Start();
}
}
else //If any other zone is activated during alarm set condition
{
if (Settings1.Default.alarmSirenDurationInMinutes != 0)
{
//Output to relays 1 & 2
spIOCard.Write("~out10=1~");
spIOCard.Write("~out11=1~");
//then close after duration from Settings1 file
globalAlarmSirenDurationTicks = 0;
tmrSoundSirens.Start();
}
}
}
}
private void tmrSoundSirensAfterDelay_Tick(object sender, EventArgs e)
{
globalAlarmEntryDurationTicks = globalAlarmEntryDurationTicks + 1;
if (globalAlarmEntryDurationTicks == Settings1.Default.alarmEntryDelayInSeconds) //Value from Settings1 file
{
spIOCard.Write("~out10=1~");
spIOCard.Write("~out11=1~");
globalAlarmEntryDurationTicks = 0;
tmrSoundSirensAfterDelay.Stop();
tmrSoundSirens.Start();
}
}
private void tmrSoundSirens_Tick(object sender, EventArgs e)
{
globalAlarmSirenDurationTicks = globalAlarmSirenDurationTicks + 1;
if (globalAlarmSirenDurationTicks == (Settings1.Default.alarmSirenDurationInMinutes * 5)) //*60 Value from Settings1 file
{
spIOCard.Write("~out10=0~");
spIOCard.Write("~out11=0~");
globalAlarmSirenDurationTicks = 0;
tmrSoundSirens.Stop();
}
}
private void button24_Click(object sender, EventArgs e)
{
globalFullAlarmSet = true;
checkAndActivateRelays(1);
}
Serial Port Data Received Code:
private void spIO_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
RxString = spIOCard.ReadExisting();
if (RxString == "~in00=1~")
{
checkAndActivateRelays(1);
button10.BackColor = System.Drawing.Color.Red;
}
if (RxString == "~in00=0~")
{
button10.BackColor = System.Drawing.Color.LightGray;
}
if (RxString == "~in01=1~")
{
checkAndActivateRelays(2);
button11.BackColor = System.Drawing.Color.Red;
}
if (RxString == "~in01=0~")
{
button11.BackColor = System.Drawing.Color.LightGray;
}
if (RxString == "~in02=1~")
{
button12.BackColor = System.Drawing.Color.Red;
}
if (RxString == "~in02=0~")
{
button12.BackColor = System.Drawing.Color.LightGray;
}
}
Something to think about since you are using the DataReceivedEvent. According to MSDN it is raised on a secondary thread. This is probably causing your issue.
The DataReceived event is raised on a secondary thread when data is
received from the SerialPort object. Because this event is raised on a
secondary thread, and not the main thread, attempting to modify some
elements in the main thread, such as UI elements, could raise a
threading exception. If it is necessary to modify elements in the main
Form or Control, post change requests back using Invoke, which will do
the work on the proper thread.
Since calling Start() is not the problem the timer setup is where you need to look. Make sure you handle the tick event AND set an interval.
myTimer.Tick += new EventHandler(TimerEventProcessor);
// Sets the timer interval to 5 seconds.
myTimer.Interval = 5000;
myTimer.Start();
The key here is that you are doing this in the SerialPort DataReceived event. This event is fired on a separate thread. Thats important because you probably registered for the Tick event on the main thread, but you start the timer on a different one. You'll need to register the Tick event in the checkAndActivateRelays function. Then it should be happy.
The DataReceived event is raised on a secondary thread when data is received from the SerialPort object. Because this event is raised on a secondary thread, and not the main thread, attempting to modify some elements in the main thread, such as UI elements, could raise a threading exception. If it is necessary to modify elements in the main Form or Control, post change requests back using Invoke, which will do the work on the proper thread.
I have a Quartz.NET application where I need the administrators to be able to modify the job details - mostly information in each jobs datamap, but also things like the triggers - here is my code I'm using
protected void ButtonSubmit_Click(object sender, EventArgs e)
{
JobDetail jobDetail = sched.GetJobDetail(hdnID.Value, hdnGroupID.Value);
jobDetail.JobDataMap["idname"] = txtName.Text;
jobDetail.JobDataMap["initialPath"] = TextBox1.Text;
jobDetail.JobDataMap["targetPath"] = TextBox2.Text;
jobDetail.JobDataMap["regex"] = TextBox3.Text;
jobDetail.JobDataMap["overrideemails"] = txtEmails.Text;
jobDetail.JobDataMap["flush"] = chkflush.Checked;
jobDetail.JobDataMap["impUsername"] = txtImpUsername.Text;
jobDetail.JobDataMap["impDomain"] = txtImpDomain.Text;
jobDetail.JobDataMap["impPassword"] = txtImpPassword.Text;
Trigger[] triggers = sched.GetTriggersOfJob(hdnID.Value, hdnGroupID.Value);
if (ddlScheduleType.SelectedIndex == 0)
{
foreach (SimpleTrigger trigger in triggers.OfType<SimpleTrigger>())
{
if (ddlInterval.SelectedIndex == 0)
{
trigger.RepeatInterval = TimeSpan.Parse("00:00:01");
}
else if (ddlInterval.SelectedIndex == 1)
{
trigger.RepeatInterval = TimeSpan.Parse("00:01:00");
}
else if (ddlInterval.SelectedIndex == 2)
{
trigger.RepeatInterval = TimeSpan.Parse("00:00:01");
}
}
}
else
{
foreach (CronTrigger trigger in triggers.OfType<CronTrigger>())
{
trigger.CronExpressionString = txtCron.Text;
}
}
}
(I know what I'm doing with the foreach loops is stupid, but there is only ever one trigger with a job and it's a snippet of code I recieved here).
Problem is, the page posts back fine and the new values still stay in the textboxes. But when I go view the job again, nothing changes at all. What am I doing wrong? It's confusing as there are no errors at all.
Note the hiddenfields are also correctly set.
Thanks
The ButtonSubmit_Click event is certainly working as I've debugged the program and the program goes through that.
The instance you get by calling sched.GetTriggersOfJob and sched.GetJobDetail are clones of the real triggers / jobs.
Your changes to those objects are not used by the scheduler until you reschedule the changed trigger or add a the changed job with the changed trigger.
I think you should be able to use RescheduleJob if you only change the triggers and you could remove the original trigger and add a new one.