I write simple audio player for Android. I wrote basic functional and now I want create notification for control audio player. I use Android classes Service and Notification and RemoteViews for big and small notifications. This work good, but, when I use fasted flipping for audio tracks my notification don't update text with name audio tracks. I create simple video for demonstration problem:
you-tube.com
I use C# and Xamarin Forms, but it doesn't matter, since the code is very similar to Java. My service:
[Service]
public class PlayerService : Service, IPlayerService
{
private const string ACTION_SHOW_PLAYER = "music.player.ACTION.SHOW_PLAYER";
private const string ACTION_PLAY_PAUSE = "music.player.ACTION.PLAY_PAUSE";
private const string ACTION_PLAY_PREW = "music.player.ACTION.PLAY_PREW";
private const string ACTION_PLAY_NEXT = "music.player.ACTION.PLAY_NEXT";
private const string ACTION_STOP_SERVICE = "music.player.ACTION.STOP_SERVICE";
private const string ANDROID_CHANNEL_ID = "com.companyname.homeremotecontroller";
private const int NOTIFICATION_ID = 46;
private RemoteViews _remoteViewsBig = null;
private RemoteViews _remoteViewsSmall = null;
private NotificationManager _notificationManager = null;
private NotificationCompat.Builder _builder = null;
private static IPlayerService _instance = null;
private static bool _isActiveBackgroundService = false;
private static bool _isBeenStartBackgroundService= false;
public IPlayerService GetInstance()
{
return _instance;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
base.OnCreate();
_instance = this;
MusicPlaylistModel.GetInstance().ChangeSound += () =>
{
UpdateSound(MusicPlaylistModel.GetInstance().CurrentSound.SoundName);
};
MusicPlaylistModel.GetInstance().MusicPlayer.TimeSound += (start, end) =>
{
// TODO: empty
};
MusicPlaylistModel.GetInstance().MusicPlayer.TickCurrentSound += (curr) =>
{
UpdateProgressSound(curr);
};
MusicPlaylistModel.GetInstance().PlayingStatus += (status) =>
{
UpdatePlayPauseStatus(status);
};
_notificationManager = ((NotificationManager)GetSystemService(NotificationService));
_notificationManager.CreateNotificationChannel(new NotificationChannel(ANDROID_CHANNEL_ID, "Audio player", NotificationImportance.High));
Intent notificationIntent = new Intent(MainActivity.Activity, typeof(MainActivity));
_remoteViewsSmall = GetSmallContentView();
_remoteViewsBig = GetBigContentView();
CreateBuilderNotification();
}
public override void OnDestroy()
{
base.OnDestroy();
}
public void CreateBuilderNotification()
{
_builder = new NotificationCompat.Builder(MainActivity.Activity, ANDROID_CHANNEL_ID)
.SetCategory(Notification.ExtraMediaSession)
.SetCustomBigContentView(_remoteViewsBig)
.SetCustomContentView(_remoteViewsSmall)
.SetSmallIcon(Resource.Mipmap.icon)
.SetOngoing(true)
.SetSilent(true)
.SetPriority((int)NotificationPriority.Max)
.SetVisibility(NotificationCompat.VisibilityPublic)
.SetAutoCancel(true)
.SetStyle(new NotificationCompat.DecoratedCustomViewStyle());
}
public Notification RecreateNotification()
{
return _builder.Build();
}
private RemoteViews GetBigContentView()
{
RemoteViews mContentViewBig = new RemoteViews(MainActivity.Activity.PackageName, Resource.Layout.player_notify_big);
Intent closeIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
closeIntent.SetAction(ACTION_STOP_SERVICE);
PendingIntent pcloseIntent = PendingIntent.GetService(MainActivity.Activity, 0, closeIntent, 0);
Intent playPauseIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
playPauseIntent.SetAction(ACTION_PLAY_PAUSE);
PendingIntent pPlayPauseIntent = PendingIntent.GetService(MainActivity.Activity, 0, playPauseIntent, 0);
Intent nextIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
nextIntent.SetAction(ACTION_PLAY_NEXT);
PendingIntent pNextIntent = PendingIntent.GetService(MainActivity.Activity, 0, nextIntent, 0);
Intent prewIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
prewIntent.SetAction(ACTION_PLAY_PREW);
PendingIntent pPrewIntent = PendingIntent.GetService(MainActivity.Activity, 0, prewIntent, 0);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPauseMusicBig, pPlayPauseIntent);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.btnWidgetCloseService, pcloseIntent);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.playNextBig, pNextIntent);
mContentViewBig.SetOnClickPendingIntent(Resource.Id.playPrevBig, pPrewIntent);
return mContentViewBig;
}
private RemoteViews GetSmallContentView()
{
RemoteViews mContentViewSmall = new RemoteViews(MainActivity.Activity.PackageName, Resource.Layout.player_notify_small);
Intent closeIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
closeIntent.SetAction(ACTION_STOP_SERVICE);
PendingIntent pcloseIntent = PendingIntent.GetService(MainActivity.Activity, 0, closeIntent, 0);
Intent playPauseIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
playPauseIntent.SetAction(ACTION_PLAY_PAUSE);
PendingIntent pPlayPauseIntent = PendingIntent.GetService(MainActivity.Activity, 0, playPauseIntent, 0);
Intent nextIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
nextIntent.SetAction(ACTION_PLAY_NEXT);
PendingIntent pNextIntent = PendingIntent.GetService(MainActivity.Activity, 0, nextIntent, 0);
Intent prewIntent = new Intent(MainActivity.Activity, typeof(PlayerService));
prewIntent.SetAction(ACTION_PLAY_PREW);
PendingIntent pPrewIntent = PendingIntent.GetService(MainActivity.Activity, 0, prewIntent, 0);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetCloseServiceSmall, pcloseIntent);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPauseMusic, pPlayPauseIntent);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayNext, pNextIntent);
mContentViewSmall.SetOnClickPendingIntent(Resource.Id.btnWidgetPlayPrevious, pPrewIntent);
return mContentViewSmall;
}
public void RedrawNotification()
{
if(!_isActiveBackgroundService)
{
return;
}
var notification = RecreateNotification();
notification.Flags = NotificationFlags.NoClear | NotificationFlags.OngoingEvent;
_notificationManager.Notify(NOTIFICATION_ID, notification);
StartForeground(NOTIFICATION_ID, notification);
}
public void Start()
{
MainActivity.Activity.StartForegroundService(new Intent(MainActivity.Activity, typeof(PlayerService)));
_isActiveBackgroundService = true;
_isBeenStartBackgroundService = true;
}
public void Stop()
{
MainActivity.Activity.StopService(new Intent(MainActivity.Activity, typeof(PlayerService)));
_isActiveBackgroundService = false;
}
public void UpdateSound(string snd_name)
{
_remoteViewsSmall.SetTextViewText(Resource.Id.lblWidgetCurrentMusicName, snd_name);
_remoteViewsBig.SetTextViewText(Resource.Id.lblWidgetCurrentMusicName, snd_name);
_builder.SetCustomBigContentView(_remoteViewsBig)
.SetCustomContentView(_remoteViewsSmall);
RedrawNotification();
}
public void UpdateProgressSound(int currProgress)
{
// TODO: empty
}
public void UpdatePlayPauseStatus(bool status)
{
if(status)
{
_remoteViewsSmall.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusic, Resource.Drawable.play_off);
_remoteViewsBig.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusicBig, Resource.Drawable.play_off);
}
else
{
_remoteViewsSmall.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusic, Resource.Drawable.pause_off);
_remoteViewsBig.SetImageViewResource(Resource.Id.btnWidgetPlayPauseMusicBig, Resource.Drawable.pause_off);
}
_builder.SetCustomBigContentView(_remoteViewsBig)
.SetCustomContentView(_remoteViewsSmall);
RedrawNotification();
}
public override StartCommandResult OnStartCommand(Intent? intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if (_isBeenStartBackgroundService)
{
UpdateSound(MusicPlaylistModel.GetInstance().CurrentSound.SoundName);
UpdatePlayPauseStatus(false);
RedrawNotification();
_isBeenStartBackgroundService = false;
}
string action = intent.Action;
if (action != null)
{
if (action == ACTION_STOP_SERVICE)
{
Stop();
}
else
{
new Task(async () =>
{
if (action == ACTION_PLAY_PAUSE)
{
await MusicPlaylistModel.GetInstance().Play();
return;
}
else if (action == ACTION_PLAY_PREW)
{
await MusicPlaylistModel.GetInstance().Prew();
return;
}
else if (action == ACTION_PLAY_NEXT)
{
await MusicPlaylistModel.GetInstance().Next();
return;
}
}).Start();
}
}
return StartCommandResult.Sticky;
}
}
To show the notification, the external code calls the Start method, and to hide the notification, the external code calls the Stop method.
I check for debugging. Application successful will hit in method UpdateSound, where snd_name - is correct. But text in Notification is not correct.
How fix it?
In UpdateSound() you call RedrawNotification()
In RedrawNotification() you call RecreateNotification()
In RecreateNotification() you call _builder.Build()
But you never made any changes in _builder.
The solution: use Notification.MediaStyle class.
Related
I am trying to collect location data from my own android phone for a project, but so far I'm not successful in letting functions run in the background.
I have tried the following:
Run repeated functions OnSleep()
This process automatically stopped after a few minutes
Use a Foreground Service with a repeated function inside of it
This is what I attempted with my Foreground Service:
AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
MainActivity.cs
[Activity(Label = "LocationApp", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Activity ActivityCurrent { get; private set;}
protected override void OnCreate(Bundle savedInstanceState)
{
ActivityCurrent = this;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
RequestPermissions();
}
private async void RequestPermissions()
{
await CrossPermissions.Current.RequestPermissionAsync<StoragePermission>();
await CrossPermissions.Current.RequestPermissionAsync<LocationAlwaysPermission>();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
TracingServices.cs
[assembly:Xamarin.Forms.Dependency(typeof(LocationApp.Droid.TracingServices))]
namespace LocationApp.Droid
{
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)]
public class TracingServices : Service,ITracingServices
{
private List<Timer> Timerlist = new List<Timer>();
private double initialLon;
private double initialLat;
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if(intent.Action=="START_SERVICE")
{
Console.WriteLine("Started service");
RegisterNotification();
TrackLocation();
}
else if(intent.Action=="STOP_SERVICE")
{
Console.WriteLine("Stopping service");
StopForeground(true);
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(TracingServices));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
stopIntent.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(stopIntent);
// stop the timer
foreach (Timer timer in Timerlist)
{
timer.Stop();
}
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("Servicechannel", "Service demo", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "Servicechannel")
.SetContentTitle("Testing service")
.SetOngoing(true)
.Build();
StartForeground(100, notification);
}
private async void TrackLocation()
{
// track initial coordinates
Location result = await Geolocation.GetLocationAsync();
initialLat = result.Latitude;
initialLon = result.Longitude;
// set timer to launch every 3 minutes
Timer timer = new Timer(3 * 60 * 1000);
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, initialLat, initialLon);
timer.AutoReset = true;
timer.Enabled = true;
Timerlist.Add(timer);
}
private static async void OnTimedEvent(object source, ElapsedEventArgs e, double lat, double lon)
{
Location result = await Geolocation.GetLocationAsync();
Console.WriteLine($"Final stuff: {result.Latitude},{result.Longitude},{result.Timestamp.DateTime:dd/MM},{result.Timestamp.DateTime:HH:mm}");
await App.Database.SaveLocationAsync(new LocationData
{
Latitude = result.Latitude - lat,
Longitude = result.Longitude - lon,
Day = result.Timestamp.DateTime.ToString("dd/MM"),
Time = result.Timestamp.DateTime.ToString("HH:mm")
});
}
}
}
MainPage.xaml.cs function
async private void ToggleTracking(object send, EventArgs args)
{
var status = await SecureStorage.GetAsync("Tracking");
// if the status starts as false, set it to true, update the button text and execute the Foreground service
if (status != "true")
{
await SecureStorage.SetAsync("Tracking", "true");
ToggleButton.Text = "Stop tracking";
DependencyService.Get<ITracingServices>().Start();
}
// if the status starts as true, set it to false, update the button text and end the Foreground service
else
{
await SecureStorage.SetAsync("Tracking", "false");
ToggleButton.Text = "Start tracking";
DependencyService.Get<ITracingServices>().Stop();
}
}
I am sure the ToggleTracking function is called, as my device displays the Foreground Service as being active, but the function isn't being executed.
Is it possible to edit the Foreground Service so that it still runs the function with an interval or is there a different option that can run functions at an interval in the background?
Changing the order of operations in the TracingServices class has caused the application to be called, though not with regular interval. Sometimes it executes, sometimes it doesn't for a few intervals. This is how I changed the order:
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)]
public class TracingServices : Service,ITracingServices
{
private List<Timer> Timerlist = new List<Timer>();
private double initialLon;
private double initialLat;
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if(intent.Action=="START_SERVICE")
{
Console.WriteLine("Started service");
RegisterNotification();
}
else if(intent.Action=="STOP_SERVICE")
{
Console.WriteLine("Stopping service");
StopForeground(true);
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(TracingServices));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent stopIntent = new Intent(MainActivity.ActivityCurrent, this.Class);
stopIntent.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(stopIntent);
// stop the timer
foreach (Timer timer in Timerlist)
{
timer.Stop();
}
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("Servicechannel", "Service demo", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "Servicechannel")
.SetContentTitle("Testing service")
.SetOngoing(true)
.Build();
StartForeground(100, notification);
TrackLocation();
}
private async void TrackLocation()
{
Console.WriteLine("Tracking called");
// track initial coordinates
Location result = await Geolocation.GetLocationAsync();
initialLat = result.Latitude;
initialLon = result.Longitude;
// set timer to launch every 3 minutes
Timer timer = new Timer(3 * 60 * 1000);
timer.Elapsed += (sender, e) => OnTimedEvent(sender, e, initialLat, initialLon);
timer.AutoReset = true;
timer.Enabled = true;
Console.WriteLine("Timer set");
Timerlist.Add(timer);
}
private static async void OnTimedEvent(object source, ElapsedEventArgs e, double lat, double lon)
{
Location result = await Geolocation.GetLocationAsync();
Console.WriteLine($"Final stuff: {result.Latitude},{result.Longitude},{result.Timestamp.DateTime:dd/MM},{result.Timestamp.DateTime:HH:mm}");
await App.Database.SaveLocationAsync(new LocationData
{
Latitude = result.Latitude - lat,
Longitude = result.Longitude - lon,
Day = result.Timestamp.DateTime.ToString("dd/MM"),
Time = result.Timestamp.DateTime.ToString("HH:mm")
});
}
}
If there is a way for the function to fire every time at an interval, I'd love to hear it.
In Unity3d Webgl I download and write big file (about 100 mb and more) to cache (indexeddb).
I downloaded file with UnityWebRequest and write file in downloadHandler.
Sometimes after call filestream.Write() or filestream.Flush() Chrome will crash.
Exception not available, only crash tab of chrome.
Crash happens abount in 50 percent of dowloads.
public class Downloader : MonoBehaviour
{
public IEnumerator DownloadFileCoroutine(string url, string filePath, string clientName, int fileId, Action<int> downloadedCallback)
{
var currentWebRequest = UnityWebRequest.Get(url);
currentWebRequest.downloadHandler = new ToFileDownloadHandler(new byte[64 * 1024], filePath);
var downloadHendler = (currentWebRequest.downloadHandler as ToFileDownloadHandler);
currentWebRequest.Send();
while (!currentWebRequest.isDone && !currentWebRequest.isNetworkError && !currentWebRequest.isHttpError)
{
if (currentWebRequest.isNetworkError)
{
yield break;
}
}
if (currentWebRequest.isNetworkError)
{
downloadHendler.Cancel();
}
else
{
/// suceess
}
}
public class ToFileDownloadHandler : DownloadHandlerScript
{
private int _expected = -1;
private long _received = 0;
private readonly string _filepath;
private readonly FileStream _fileStream = null;
private bool _canceled = false;
public ToFileDownloadHandler(byte[] buffer, string filepath) : base(buffer)
{
CreateDir();
_filepath = filepath;
_fileStream = new FileStream(filepath, FileMode.Create, FileAccess.Write);
}
protected override byte[] GetData() { return null; }
protected override bool ReceiveData(byte[] data, int dataLength)
{
if (data == null || data.Length < 1)
{
return false;
}
_received += dataLength;
if (!_canceled)
{
_fileStream.Write(data, 0, dataLength);
_fileStream.Flush();
}
return true;
}
protected override float GetProgress()
{
if (_expected < 0) return 0;
return (float)_received / _expected;
}
protected override void CompleteContent()
{
_fileStream.Close();
_isComplete = true;
}
protected override void ReceiveContentLength(int contentLength)
{
_expected = contentLength;
}
public void Cancel()
{
if (_canceled) return;
_canceled = true;
if (_fileStream != null)
{
_fileStream.Close();
}
File.Delete(_filepath);
}
private void CreateDir()
{
cachePath = Application.persistentDataPath;
if (!Directory.Exists(cachePath))
{
Directory.CreateDirectory(cachePath);
}
}
}
}
I'd like to create notificiation in my app, which is going to be showed in 10 seconds. It works well, when application is running, but when I close the application, notification is not showed. Here is my code:
My notification service:
[Service]
class NotifyEvent : IntentService
{
protected override void OnHandleIntent(Intent intent)
{
PendingIntent pIntent = PendingIntent.GetActivity(this, 0, intent, 0);
Notification.Builder builder = new Notification.Builder(this);
builder.SetContentTitle(Resources.GetString(Resource.String.NotifikaceNadpis));
builder.SetContentText(Resources.GetString(Resource.String.NotifikaceText));
builder.SetSmallIcon(Resource.Drawable.Icon);
builder.SetPriority(1);
builder.SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate);
builder.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis());
Notification notifikace = builder.Build();
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
const int notificationId = 0;
notificationManager.Notify(notificationId, notifikace);
}
}
Class, which starts notification:
public class Notificator
{
public void ShowNotification(Context context)
{
Intent intent = new Intent(context, typeof(NotifyEvent));
var pendingServiceIntent = PendingIntent.GetService(context, 0, intent, PendingIntentFlags.UpdateCurrent);
AlarmManager alarm = (AlarmManager)context.GetSystemService(Context.AlarmService);
alarm.Set(AlarmType.ElapsedRealtimeWakeup, SystemClock.ElapsedRealtime() + 10000, pendingServiceIntent);
}
}
Method in activity:
Notificator not = new Notificator();
not.ShowNotification(this);
My Activity:
[Activity(Label = "Nastavení")]
public class SettingsActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
SetContentView(Resource.Layout.Settings);
Button vynulovatButton = FindViewById<Button>(Resource.Id.buttonRestartDne);
vynulovatButton.Click += VynulovatDen;
}
...
protected void VynulovatDen(object sender, EventArgs e)
{
Notificator not = new Notificator();
not.ShowNotification(this);
}
}
Thanks for every help.
you can try this.
protected override void OnDestroy()
{
Notificator not = new Notificator();
not.ShowNotification(this);
base.OnDestroy();
}
You should keep your service alive when you destroy your application.
add return StartCommandResult.Sticky; in the OnStartCommand method.
start the service OnTaskRemoved function.
Create your service with the Service interface, the IntentService is for Time-consuming operation.
class NotifyEvent : Service
{
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
new Task(() => {
PendingIntent pIntent = PendingIntent.GetActivity(this, 0, intent, 0);
Notification.Builder builder = new Notification.Builder(this);
builder.SetContentTitle("hello");
builder.SetContentText("hello");
builder.SetSmallIcon(Resource.Drawable.Icon);
builder.SetPriority(1);
builder.SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate);
builder.SetWhen(Java.Lang.JavaSystem.CurrentTimeMillis());
Notification notifikace = builder.Build();
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
const int notificationId = 0;
notificationManager.Notify(notificationId, notifikace);
}).Start();
return StartCommandResult.Sticky;
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnTaskRemoved(Intent rootIntent)
{
Intent restartService = new Intent(ApplicationContext, typeof(NotifyEvent));
restartService.SetPackage(PackageName);
var pendingServiceIntent = PendingIntent.GetService(ApplicationContext, 0, restartService, PendingIntentFlags.UpdateCurrent);
AlarmManager alarm = (AlarmManager)ApplicationContext.GetSystemService(Context.AlarmService);
alarm.Set(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime() + 1000, pendingServiceIntent);
System.Console.WriteLine("service OnTaskRemoved");
base.OnTaskRemoved(rootIntent);
}
}
I'm creating an Windowns phone 8 app(c#), its a countdown interval timer, so there is prepare time(10 sec), work time(20 sec), rest time(10 sec). I have these variables
`TimeSpan prepInterval = new TimeSpan(0, 0, 0, 10);
TimeSpan workInterval = new TimeSpan(0, 0, 0, 20);
TimeSpan restInterval = new TimeSpan(0, 0, 0, 10);`
I can't wrap my head around having them implementing them one after another when they hit 0. So when prepare time is done, the work timer is to start and when thats finised, the rest timer is to start.
If you would like to have some more broken down logic in all of this, maybe you can create some classes based on a simple interface, like the following:
interface ITimerAction
{
int Seconds { get; set; }
bool Started { get; }
bool Completed { get; }
void OnStart();
void OnComplete();
}
interface ITimerActionList
{
void Add(ITimerAction action);
void Work();
event EventHandler OnCompletedEvent;
}
This would then allow you to create an abstract TimerAction class, and TimerActionList
abstract class TimerAction : ITimerAction
{
public virtual int Seconds
{
get;
set;
}
public virtual bool Completed
{
get;
protected set;
}
public virtual bool Started
{
get;
protected set;
}
public abstract void OnStart();
public abstract void OnComplete();
}
class TimerActionList : ITimerActionList
{
public event EventHandler OnCompletedEvent;
private readonly IList<ITimerAction> actions = new List<ITimerAction>();
private bool working = false;
private Thread myThread;
public void Add(ITimerAction action)
{
if (working)
{
throw new InvalidOperationException("Cannot add new timers when work is already in progress");
}
actions.Add(action);
}
protected virtual void DoWork()
{
working = true;
int currentStep = 0, maxSteps = actions.Count;
while (currentStep < maxSteps)
{
ITimerAction action = actions[currentStep];
if (!action.Started)
{
action.OnStart();
}
if (action.Completed)
{
currentStep++;
continue;
}
if (action.Seconds == 0)
{
action.OnComplete();
continue;
}
action.Seconds--;
Thread.Sleep(1000);
}
Completed();
}
public void Work()
{
if (working)
{
throw new InvalidOperationException("Already running!");
}
working = true;
myThread = new Thread(DoWork);
myThread.Start();
}
protected virtual void Completed()
{
myThread = null;
working = false;
actions.Clear();
var local = OnCompletedEvent;
if (local != null)
{
local.Invoke(this, EventArgs.Empty);
}
}
}
You could then write the classes that inherit from the TimerAction class, that could handle an action before and after the timer ran through :)
class PrepareTimer : TimerAction
{
public override void OnStart()
{
Console.WriteLine("Preparing");
Started = true;
}
public override void OnComplete()
{
Console.WriteLine("Prepare ready");
Completed = true;
}
}
class WorkTimer : TimerAction
{
public override void OnStart()
{
Console.WriteLine("Working");
Started = true;
}
public override void OnComplete()
{
Console.WriteLine("Work ready");
Completed = true;
}
}
class CoolDownTimer : TimerAction
{
public override void OnStart()
{
Console.WriteLine("Cooling down");
Started = true;
}
public override void OnComplete()
{
Console.WriteLine("Cooldown ready");
Completed = true;
}
}
And then you could test the code as such
static void Main(string[] args)
{
bool done = false;
ITimerActionList mylist = new TimerActionList();
mylist.Add(new PrepareTimer { Seconds = 1 });
mylist.Add(new WorkTimer { Seconds = 2 });
mylist.Add(new CoolDownTimer { Seconds = 1 });
mylist.OnCompletedEvent += (sender, e) =>
{
done = true;
};
mylist.Work();
while (!done)
{
// timer is running
}
Console.WriteLine("Done!");
}
(Console program, but i guess that also goes to demonstrate?)
Here's an example based on deathismyfriend's and Hans Passant's suggestions:
var start = new DateTime();
var stage = 0;
var timer = new System.Timers.Timer(100);
timer.Elapsed += (s, e) =>
{
var elapsed = DateTime.Now - start;
int duration = stage == 1 ? 20 : 10;
if (elapsed.TotalSeconds > duration)
{
start = DateTime.Now;
stage++;
if (stage > 2)
timer.Stop();
}
};
start = DateTime.Now;
stage = 0;
timer.Start();
I have a program that when the user says "Start" or "Stop", the program makes a skeleton display on the screen. I use the same code as the Shape Game, and it works fine there, but not on my code. I dont know which part of the code doesn't work since this is my first time eith speech recognition programming. Thanks for your help(Sorry if my code is messy)Recognizing the Speech
public class SpeechRecognizer : IDisposable
{
private KinectAudioSource kinectAudioSource;
private struct WhatSaid
{
public Verbs Verb;
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this.Stop();
if (this.sre != null)
{
// NOTE: The SpeechRecognitionEngine can take a long time to dispose
// so we will dispose it on a background thread
ThreadPool.QueueUserWorkItem(
delegate(object state)
{
IDisposable toDispose = state as IDisposable;
if (toDispose != null)
{
toDispose.Dispose();
}
},
this.sre);
this.sre = null;
}
this.isDisposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public EchoCancellationMode EchoCancellationMode
{
get
{
this.CheckDisposed();
return this.kinectAudioSource.EchoCancellationMode;
}
set
{
this.CheckDisposed();
this.kinectAudioSource.EchoCancellationMode = value;
}
}
public static SpeechRecognizer Create()
{
SpeechRecognizer recognizer = null;
try
{
recognizer = new SpeechRecognizer();
}
catch (Exception)
{
// speech prereq isn't installed. a null recognizer will be handled properly by the app.
}
return recognizer;
}
private void CheckDisposed()
{
if (this.isDisposed)
{
throw new ObjectDisposedException("SpeechRecognizer");
}
}
public void Stop()
{
this.CheckDisposed();
if (this.sre != null)
{
this.kinectAudioSource.Stop();
this.sre.RecognizeAsyncCancel();
this.sre.RecognizeAsyncStop();
this.sre.SpeechRecognized -= this.SreSpeechRecognized;
this.sre.SpeechHypothesized -= this.SreSpeechHypothesized;
this.sre.SpeechRecognitionRejected -= this.SreSpeechRecognitionRejected;
}
}
public void Start(KinectAudioSource kinectSource)
{
this.CheckDisposed();
this.kinectAudioSource = kinectSource;
this.kinectAudioSource.AutomaticGainControlEnabled = false;
this.kinectAudioSource.BeamAngleMode = BeamAngleMode.Adaptive;
var kinectStream = this.kinectAudioSource.Start();
this.sre.SetInputToAudioStream(
kinectStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
this.sre.RecognizeAsync(RecognizeMode.Multiple);
}
public enum Verbs
{
None = 0,
Start,
Stop,
Resume,
Pause
}
private bool isDisposed;
private readonly Dictionary<string, WhatSaid> speechCommands = new Dictionary<string, WhatSaid>
{
{ "Start", new WhatSaid { Verb = Verbs.Start } },
{ "Stop", new WhatSaid { Verb = Verbs.Stop } },
{ "Resume", new WhatSaid { Verb = Verbs.Resume } },
{ "Pause", new WhatSaid { Verb = Verbs.Pause } },
};
private SpeechRecognitionEngine sre;
private static RecognizerInfo GetKinectRecognizer()
{
Func<RecognizerInfo, bool> matchingFunc = r =>
{
string value;
r.AdditionalInfo.TryGetValue("Kinect", out value);
return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) && "en-US".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
};
return SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
}
private SpeechRecognizer()
{
RecognizerInfo ri = GetKinectRecognizer();
this.sre = new SpeechRecognitionEngine(ri);
this.LoadGrammar(this.sre);
}
private void LoadGrammar(SpeechRecognitionEngine speechRecognitionEngine)
{
// Build a simple grammar of shapes, colors, and some simple program control
var single = new Choices();
foreach (var phrase in this.speechCommands)
{
single.Add(phrase.Key);
}
var objectChoices = new Choices();
objectChoices.Add(single);
var actionGrammar = new GrammarBuilder();
actionGrammar.AppendWildcard();
actionGrammar.Append(objectChoices);
var allChoices = new Choices();
allChoices.Add(actionGrammar);
allChoices.Add(single);
// This is needed to ensure that it will work on machines with any culture, not just en-us.
var gb = new GrammarBuilder { Culture = speechRecognitionEngine.RecognizerInfo.Culture };
gb.Append(allChoices);
var g = new Grammar(gb);
speechRecognitionEngine.LoadGrammar(g);
speechRecognitionEngine.SpeechRecognized += this.SreSpeechRecognized;
speechRecognitionEngine.SpeechHypothesized += this.SreSpeechHypothesized;
speechRecognitionEngine.SpeechRecognitionRejected += this.SreSpeechRecognitionRejected;
}
private void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
var said = new SaidSomethingEventArgs { Verb = Verbs.None, Matched = "?" };
this.SetLabel("Word not Recognized.... Try 'Start', 'Stop', 'Pause' or 'Resume'");
if (this.SaidSomething != null)
{
this.SaidSomething(new object(), said);
}
}
private void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
{
this.SetLabel("I think you said: " + e.Result.Text);
}
public event EventHandler<SaidSomethingEventArgs> SaidSomething;
private void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
this.SetLabel("\rSpeech Recognized: \t" + e.Result.Text);
if ((this.SaidSomething == null) || (e.Result.Confidence < 0.3))
{
return;
}
var said = new SaidSomethingEventArgs { Verb = 0, Phrase = e.Result.Text };
foreach (var phrase in this.speechCommands)
{
if (e.Result.Text.Contains(phrase.Key) && (phrase.Value.Verb == Verbs.Pause))
{
//pause = true;
break;
}
else if ((e.Result.Text.Contains(phrase.Key) && (phrase.Value.Verb == Verbs.Resume)))
{
//resume = true;
break;
}
else if ((e.Result.Text.Contains(phrase.Key) && (phrase.Value.Verb == Verbs.Start)))
{
//start = true;
break;
}
else if ((e.Result.Text.Contains(phrase.Key) && (phrase.Value.Verb == Verbs.Stop)))
{
//stop = true;
break;
}
}
// Look for a match in the order of the lists below, first match wins.
List<Dictionary<string, WhatSaid>> allDicts = new List<Dictionary<string, WhatSaid>> { this.speechCommands };
bool found = false;
for (int i = 0; i < allDicts.Count && !found; ++i)
{
foreach (var phrase in allDicts[i])
{
if (e.Result.Text.Contains(phrase.Key))
{
found = true;
break;
}
}
}
if (!found)
{
return;
}
}
public class SaidSomethingEventArgs : EventArgs
{
public Verbs Verb { get; set; }
public string Phrase { get; set; }
public string Matched { get; set; }
}
public event Action<string> SetLabel = delegate { };
}
In my Code
private void RecognizeSaidSomething(object sender, SpeechRecognizer.SpeechRecognizer.SaidSomethingEventArgs e)
{
FlyingText.FlyingText.NewFlyingText(this.skeleton.Width / 30, new Point(this.skeleton.Width / 2, this.skeleton.Height / 2), e.Matched);
switch (e.Verb)
{
case SpeechRecognizer.SpeechRecognizer.Verbs.Pause:
pause = true;
break;
case SpeechRecognizer.SpeechRecognizer.Verbs.Resume:
resume = true;
break;
case SpeechRecognizer.SpeechRecognizer.Verbs.Start:
start = true;
break;
case SpeechRecognizer.SpeechRecognizer.Verbs.Stop:
stop = true;
break;
}
}
It doesn't look like you ever call RecognizeSaidSomething() from SreSpeechRecognized(). You create the event args:
var said = new SaidSomethingEventArgs { Verb = 0, Phrase =
e.Result.Text };
But it doesn't appear that you do anything with it.
The foreach loop below that doesn't seem to serve any purpose, you test for the phrase then just break out of the loop. You don't set any variables or call any functions in that loop that I can see.
Then there is a for loop that appears to do something similar to the foreach loop (just in a different manner). It searches for matches to the recognized phrase, but then doesn't do anything with what it finds. It just returns.
I would think somewhere in the SreSpeechRecognized() event handler you want to call RecognizeSaidSomething() and pass it the SaidSomethingEventArgs.