public static Parser <Exercise> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return (from name in HeadingParser.GetParser(2) from _ in VerticalSeparationParser.Parser from setAndRepetitionCount in setAndRepetitionCountParser from __ in VerticalSeparationParser.Parser from matchersWithActions in GetMatchersWithActionsParser(audioService, delayService, loggerService, speechService).Optional() select new Exercise( loggerService, speechService, name, setAndRepetitionCount.Item1, setAndRepetitionCount.Item2, matchersWithActions.GetOrElse(Enumerable.Empty <MatcherWithAction>()))); }
public static Parser<DoNotAwaitAction> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return from _ in Parse.IgnoreCase("don't") from __ in HorizontalWhitespaceParser.Parser.AtLeastOnce() from ___ in Parse.IgnoreCase("wait:") from ____ in VerticalSeparationParser.Parser.AtLeastOnce() from actions in ActionListParser.GetParser(indentLevel + 1, audioService, delayService, loggerService, speechService) let child = new SequenceAction(actions) select new DoNotAwaitAction( loggerService, child); }
public Exercise(ILoggerService loggerService, ISpeechService speechService, string name, int setCount, int repetitionCount, IEnumerable<MatcherWithAction> matchersWithActions) { loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); name.AssertNotNull(nameof(name)); matchersWithActions.AssertNotNull(nameof(matchersWithActions)); if (setCount < 0) { throw new ArgumentException("setCount cannot be less than zero.", "setCount"); } if (repetitionCount < 0) { throw new ArgumentException("repetitionCount cannot be less than zero.", "repetitionCount"); } this.logger = loggerService.GetLogger(this.GetType()); this.speechService = speechService; this.name = name; this.setCount = setCount; this.repetitionCount = repetitionCount; this.matchersWithActions = matchersWithActions.ToImmutableList(); using (var dummyExecutionContext = new ExecutionContext()) { this.duration = this .GetEventsWithActions(dummyExecutionContext) .SelectMany(x => x.Actions) .Select(x => x.Duration) .DefaultIfEmpty() .Aggregate((running, next) => running + next); } }
public Exercise(ILoggerService loggerService, ISpeechService speechService, string name, int setCount, int repetitionCount, IEnumerable <MatcherWithAction> matchersWithActions) { loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); name.AssertNotNull(nameof(name)); matchersWithActions.AssertNotNull(nameof(matchersWithActions)); if (setCount < 0) { throw new ArgumentException("setCount cannot be less than zero.", "setCount"); } if (repetitionCount < 0) { throw new ArgumentException("repetitionCount cannot be less than zero.", "repetitionCount"); } this.logger = loggerService.GetLogger(this.GetType()); this.speechService = speechService; this.name = name; this.setCount = setCount; this.repetitionCount = repetitionCount; this.matchersWithActions = matchersWithActions.ToImmutableList(); using (var dummyExecutionContext = new ExecutionContext()) { this.duration = this .GetEventsWithActions(dummyExecutionContext) .SelectMany(x => x.Actions) .Select(x => x.Duration) .DefaultIfEmpty() .Aggregate((running, next) => running + next); } }
public static Parser <DoNotAwaitAction> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return (from _ in Parse.IgnoreCase("don't") from __ in HorizontalWhitespaceParser.Parser.AtLeastOnce() from ___ in Parse.IgnoreCase("wait:") from ____ in VerticalSeparationParser.Parser.AtLeastOnce() from actions in ActionListParser.GetParser(indentLevel + 1, audioService, delayService, loggerService, speechService) let child = new SequenceAction(actions) select new DoNotAwaitAction( loggerService, child)); }
public static Parser <IAction> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return(BreakActionParser.GetParser(delayService, speechService) .Or <IAction>(MetronomeActionParser.GetParser(audioService, delayService, loggerService)) .Or <IAction>(PrepareActionParser.GetParser(delayService, speechService)) .Or <IAction>(SayActionParser.GetParser(speechService)) .Or <IAction>(WaitActionParser.GetParser(delayService)) .Or <IAction>(DoNotAwaitActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService)) .Or <IAction>(ParallelActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService)) .Or <IAction>(SequenceActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService))); }
public static Parser<IAction> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return BreakActionParser.GetParser(delayService, speechService) .Or<IAction>(MetronomeActionParser.GetParser(audioService, delayService, loggerService)) .Or<IAction>(PrepareActionParser.GetParser(delayService, speechService)) .Or<IAction>(SayActionParser.GetParser(speechService)) .Or<IAction>(WaitActionParser.GetParser(delayService)) .Or<IAction>(DoNotAwaitActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService)) .Or<IAction>(ParallelActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService)) .Or<IAction>(SequenceActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService)); }
public iCloudExerciseDocumentService(ILoggerService loggerService) { loggerService.AssertNotNull(nameof(loggerService)); this.logger = loggerService.GetLogger(this.GetType()); this.exerciseDocument = new BehaviorSubject<string>(null); this.sync = new object(); }
public iCloudExerciseDocumentService(ILoggerService loggerService) { loggerService.AssertNotNull(nameof(loggerService)); this.logger = loggerService.GetLogger(this.GetType()); this.exerciseDocument = new BehaviorSubject <string>(null); this.sync = new object(); }
public DoNotAwaitAction(ILoggerService loggerService, IAction innerAction) { loggerService.AssertNotNull(nameof(loggerService)); innerAction.AssertNotNull(nameof(innerAction)); this.logger = loggerService.GetLogger(this.GetType()); this.innerAction = innerAction; }
public DoNotAwaitAction(ILoggerService loggerService, IAction innerAction) { loggerService.AssertNotNull(nameof(loggerService)); innerAction.AssertNotNull(nameof(innerAction)); this.logger = loggerService.GetLogger(this.GetType()); this.innerAction = innerAction; }
public MetronomeAction(IAudioService audioService, IDelayService delayService, ILoggerService loggerService, IEnumerable <MetronomeTick> ticks) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); ticks.AssertNotNull(nameof(ticks)); this.ticks = ticks.ToImmutableList(); this.innerAction = new SequenceAction(GetInnerActions(audioService, delayService, loggerService, this.ticks)); }
public StateService(IBlobCache blobCache, ILoggerService loggerSerive) { blobCache.AssertNotNull(nameof(blobCache)); loggerSerive.AssertNotNull(nameof(loggerSerive)); _blobCache = blobCache; _logger = loggerSerive.GetLogger(this.GetType()); _saveCallbacks = new List <Func <IStateService, Task> >(); _sync = new object(); }
public StateService(IBlobCache blobCache, ILoggerService loggerService) { blobCache.AssertNotNull(nameof(blobCache)); loggerService.AssertNotNull(nameof(loggerService)); this.blobCache = blobCache; this.logger = loggerService.GetLogger(this.GetType()); this.saveCallbacks = ImmutableList <SaveCallback> .Empty; this.sync = new object(); }
public MetronomeAction(IAudioService audioService, IDelayService delayService, ILoggerService loggerService, IEnumerable<MetronomeTick> ticks) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); ticks.AssertNotNull(nameof(ticks)); this.ticks = ticks.ToImmutableList(); this.innerAction = new SequenceAction(GetInnerActions(audioService, delayService, loggerService, this.ticks)); }
public GoogleDriveExerciseDocumentService( ILoggerService loggerService, IConnectionResultHandler connectionResultHandler) { loggerService.AssertNotNull(nameof(loggerService)); connectionResultHandler.AssertNotNull(nameof(connectionResultHandler)); this.logger = loggerService.GetLogger(this.GetType()); this.connectionResultHandler = connectionResultHandler; this.exerciseDocument = new BehaviorSubject<string>(null); this.sync = new object(); this.connectedDisposable = new SerialDisposable(); }
public GoogleDriveExerciseDocumentService( ILoggerService loggerService, IConnectionResultHandler connectionResultHandler) { loggerService.AssertNotNull(nameof(loggerService)); connectionResultHandler.AssertNotNull(nameof(connectionResultHandler)); this.logger = loggerService.GetLogger(this.GetType()); this.connectionResultHandler = connectionResultHandler; this.exerciseDocument = new BehaviorSubject <string>(null); this.sync = new object(); this.connectedDisposable = new SerialDisposable(); }
public ExerciseProgram(ILoggerService loggerService, string name, IEnumerable <Exercise> exercises) { loggerService.AssertNotNull(nameof(loggerService)); name.AssertNotNull(nameof(name)); exercises.AssertNotNull(nameof(exercises), assertContentsNotNull: true); this.logger = loggerService.GetLogger(this.GetType()); this.name = name; this.exercises = exercises.ToImmutableList(); this.duration = this .exercises .Select(x => x.Duration) .DefaultIfEmpty() .Aggregate((running, next) => running + next); }
public ExerciseProgram(ILoggerService loggerService, string name, IEnumerable<Exercise> exercises) { loggerService.AssertNotNull(nameof(loggerService)); name.AssertNotNull(nameof(name)); exercises.AssertNotNull(nameof(exercises), assertContentsNotNull: true); this.logger = loggerService.GetLogger(this.GetType()); this.name = name; this.exercises = exercises.ToImmutableList(); this.duration = this .exercises .Select(x => x.Duration) .DefaultIfEmpty() .Aggregate((running, next) => running + next); }
public static IResult<ExercisePrograms> TryParse( string input, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { input.AssertNotNull(nameof(input)); audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return ExerciseProgramsParser.GetParser(audioService, delayService, loggerService, speechService).TryParse(input); }
public static Parser <ExerciseProgram> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return (from name in HeadingParser.GetParser(1) from _ in Parse.WhiteSpace.Many() from exercises in ExerciseParser.GetParser(audioService, delayService, loggerService, speechService).DelimitedBy(Parse.WhiteSpace.Many()).Optional() select new ExerciseProgram(loggerService, name, exercises.GetOrElse(Enumerable.Empty <Exercise>()))); }
public static Parser <ExercisePrograms> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return (from _ in VerticalSeparationParser.Parser from exercisePrograms in ExerciseProgramParser.GetParser(audioService, delayService, loggerService, speechService).DelimitedBy(Parse.WhiteSpace.Many()).Optional() from __ in Parse.WhiteSpace.Many().End() select new ExercisePrograms(exercisePrograms.GetOrElse(Enumerable.Empty <ExerciseProgram>()))); }
public static Parser<ExerciseProgram> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return from name in HeadingParser.GetParser(1) from _ in Parse.WhiteSpace.Many() from exercises in ExerciseParser.GetParser(audioService, delayService, loggerService, speechService).DelimitedBy(Parse.WhiteSpace.Many()).Optional() select new ExerciseProgram(loggerService, name, exercises.GetOrElse(Enumerable.Empty<Exercise>())); }
public static Parser<ExercisePrograms> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return from _ in VerticalSeparationParser.Parser from exercisePrograms in ExerciseProgramParser.GetParser(audioService, delayService, loggerService, speechService).DelimitedBy(Parse.WhiteSpace.Many()).Optional() from __ in Parse.WhiteSpace.Many().End() select new ExercisePrograms(exercisePrograms.GetOrElse(Enumerable.Empty<ExerciseProgram>())); }
public static Parser <MetronomeAction> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); return (from _ in Parse.IgnoreCase("metronome") from __ in HorizontalWhitespaceParser.Parser.AtLeastOnce() from ___ in Parse.IgnoreCase("at") from ____ in HorizontalWhitespaceParser.Parser.AtLeastOnce() from ticks in metronomeTickParser.DelimitedBy(Parse.Char(',').Token(HorizontalWhitespaceParser.Parser)) select new MetronomeAction( audioService, delayService, loggerService, ticks)); }
public static Parser<MetronomeAction> GetParser( IAudioService audioService, IDelayService delayService, ILoggerService loggerService) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); return from _ in Parse.IgnoreCase("metronome") from __ in HorizontalWhitespaceParser.Parser.AtLeastOnce() from ___ in Parse.IgnoreCase("at") from ____ in HorizontalWhitespaceParser.Parser.AtLeastOnce() from ticks in metronomeTickParser.DelimitedBy(Parse.Char(',').Token(HorizontalWhitespaceParser.Parser)) select new MetronomeAction( audioService, delayService, loggerService, ticks); }
public static Parser <ParallelAction> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return (from _ in Parse.IgnoreCase("parallel:") from __ in VerticalSeparationParser.Parser.AtLeastOnce() from actions in ActionListParser.GetParser(indentLevel + 1, audioService, delayService, loggerService, speechService) select new ParallelAction(actions)); }
public static Parser<IEnumerable<IAction>> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return (from _ in Parse.String(" ").Or(Parse.String("\t")).Repeat(indentLevel) from __ in Parse.String("* ") from action in ActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService).Token(HorizontalWhitespaceParser.Parser) select action).DelimitedBy(NewLineParser.Parser); }
public static Parser<ParallelAction> GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return from _ in Parse.IgnoreCase("parallel:") from __ in VerticalSeparationParser.Parser.AtLeastOnce() from actions in ActionListParser.GetParser(indentLevel + 1, audioService, delayService, loggerService, speechService) select new ParallelAction(actions); }
public static Parser <IEnumerable <IAction> > GetParser( int indentLevel, IAudioService audioService, IDelayService delayService, ILoggerService loggerService, ISpeechService speechService) { if (indentLevel < 0) { throw new ArgumentException("indentLevel must be greater than or equal to 0.", "indentLevel"); } audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); loggerService.AssertNotNull(nameof(loggerService)); speechService.AssertNotNull(nameof(speechService)); return ((from _ in Parse.String(" ").Or(Parse.String("\t")).Repeat(indentLevel) from __ in Parse.String("* ") from action in ActionParser.GetParser(indentLevel, audioService, delayService, loggerService, speechService).Token(HorizontalWhitespaceParser.Parser) select action).DelimitedBy(NewLineParser.Parser)); }
public ExerciseProgramViewModel( ILoggerService loggerService, ISchedulerService schedulerService, IScreen hostScreen, ExerciseProgram model) { loggerService.AssertNotNull(nameof(loggerService)); schedulerService.AssertNotNull(nameof(schedulerService)); hostScreen.AssertNotNull(nameof(hostScreen)); model.AssertNotNull(nameof(model)); this.logger = loggerService.GetLogger(this.GetType()); this.schedulerService = schedulerService; this.model = model; this.hostScreen = hostScreen; this.disposables = new CompositeDisposable(); this.exercises = this.model.Exercises.CreateDerivedCollection(x => new ExerciseViewModel(schedulerService, x, this.WhenAnyValue(y => y.ExecutionContext))); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.IsCancelled, (ec, isCancelled) => ec != null && !isCancelled) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.IsStarted = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(false) : x.WhenAnyValue(y => y.IsPaused)) .Switch() .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.IsPaused = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(TimeSpan.Zero) : x.WhenAnyValue(y => y.Progress)) .Switch() .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.ProgressTimeSpan = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ProgressTimeSpan) .Select(x => x.TotalMilliseconds / this.model.Duration.TotalMilliseconds) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.Progress = x) .AddTo(this.disposables); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.CurrentExercise, (ec, currentExercise) => ec == null ? null : currentExercise) .Select(x => this.Exercises.SingleOrDefault(y => y.Model == x)) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.CurrentExercise = x) .AddTo(this.disposables); var canStart = this .WhenAnyValue(x => x.IsStarted) .Select(x => !x) .ObserveOn(schedulerService.MainScheduler) .Do(x => System.Diagnostics.Debug.WriteLine("CanStart changing to " + x)); this.startCommand = ReactiveCommand .CreateAsyncObservable(canStart, this.OnStartAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canPause = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && !isPaused) .ObserveOn(schedulerService.MainScheduler); this.pauseCommand = ReactiveCommand .CreateAsyncObservable(canPause, this.OnPauseAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canResume = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && isPaused) .ObserveOn(schedulerService.MainScheduler); this.resumeCommand = ReactiveCommand .CreateAsyncObservable(canResume, this.OnResumeAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canSkipBackwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.ProgressTimeSpan, (ec, progress) => new { ExecutionContext = ec, Progress = progress }) .Select(x => x.ExecutionContext != null && x.Progress >= skipBackwardsThreshold) .ObserveOn(schedulerService.MainScheduler); this.skipBackwardsCommand = ReactiveCommand .CreateAsyncObservable(canSkipBackwards, this.OnSkipBackwardsAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canSkipForwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.CurrentExercise, (ec, currentExercise) => new { ExecutionContext = ec, CurrentExercise = currentExercise }) .Select(x => x.ExecutionContext != null && x.CurrentExercise != null && x.CurrentExercise != this.exercises.LastOrDefault()) .ObserveOn(schedulerService.MainScheduler); this.skipForwardsCommand = ReactiveCommand .CreateAsyncObservable(canSkipForwards, this.OnSkipForwardsAsync, schedulerService.MainScheduler) .AddTo(this.disposables); this.startCommand .CanExecuteObservable .Subscribe(x => this.IsStartVisible = x) .AddTo(this.disposables); this.pauseCommand .CanExecuteObservable .Subscribe(x => this.IsPauseVisible = x) .AddTo(this.disposables); this.resumeCommand .CanExecuteObservable .Subscribe(x => this.IsResumeVisible = x) .AddTo(this.disposables); this.startCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("start", ex)) .AddTo(this.disposables); this.pauseCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("pause", ex)) .AddTo(this.disposables); this.resumeCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("resume", ex)) .AddTo(this.disposables); this.skipBackwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip backwards", ex)) .AddTo(this.disposables); this.skipForwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip forwards", ex)) .AddTo(this.disposables); // we don't use a reactive command here because switching in different commands causes it to get confused and // command binding leaves the target button disabled. We could also have not used command binding to get around // this problem this.playbackCommand = new PlaybackCommandImpl(this); // cancel the exercise program if the user navigates away this .hostScreen .Router .NavigationStack .ItemsRemoved .OfType <ExerciseProgramViewModel>() .SelectMany(x => x.StopAsync()) .Subscribe() .AddTo(this.disposables); }
public ExerciseProgramViewModel( ILoggerService loggerService, ISchedulerService schedulerService, IScreen hostScreen, ExerciseProgram model) { loggerService.AssertNotNull(nameof(loggerService)); schedulerService.AssertNotNull(nameof(schedulerService)); hostScreen.AssertNotNull(nameof(hostScreen)); model.AssertNotNull(nameof(model)); this.logger = loggerService.GetLogger(this.GetType()); this.schedulerService = schedulerService; this.model = model; this.hostScreen = hostScreen; this.disposables = new CompositeDisposable(); this.exercises = this.model.Exercises.CreateDerivedCollection(x => new ExerciseViewModel(schedulerService, x, this.WhenAnyValue(y => y.ExecutionContext))); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.IsCancelled, (ec, isCancelled) => ec != null && !isCancelled) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.IsStarted = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(false) : x.WhenAnyValue(y => y.IsPaused)) .Switch() .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.IsPaused = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ExecutionContext) .Select(x => x == null ? Observable.Return(TimeSpan.Zero) : x.WhenAnyValue(y => y.Progress)) .Switch() .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.ProgressTimeSpan = x) .AddTo(this.disposables); this .WhenAnyValue(x => x.ProgressTimeSpan) .Select(x => x.TotalMilliseconds / this.model.Duration.TotalMilliseconds) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.Progress = x) .AddTo(this.disposables); this .WhenAnyValue( x => x.ExecutionContext, x => x.ExecutionContext.CurrentExercise, (ec, currentExercise) => ec == null ? null : currentExercise) .Select(x => this.Exercises.SingleOrDefault(y => y.Model == x)) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.CurrentExercise = x) .AddTo(this.disposables); var canStart = this .WhenAnyValue(x => x.IsStarted) .Select(x => !x) .ObserveOn(schedulerService.MainScheduler) .Do(x => System.Diagnostics.Debug.WriteLine("CanStart changing to " + x)); this.startCommand = ReactiveCommand .CreateAsyncObservable(canStart, this.OnStartAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canPause = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && !isPaused) .ObserveOn(schedulerService.MainScheduler); this.pauseCommand = ReactiveCommand .CreateAsyncObservable(canPause, this.OnPauseAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canResume = this .WhenAnyValue(x => x.IsStarted) .CombineLatest(this.WhenAnyValue(x => x.ExecutionContext.IsPaused), (isStarted, isPaused) => isStarted && isPaused) .ObserveOn(schedulerService.MainScheduler); this.resumeCommand = ReactiveCommand .CreateAsyncObservable(canResume, this.OnResumeAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canSkipBackwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.ProgressTimeSpan, (ec, progress) => new { ExecutionContext = ec, Progress = progress }) .Select(x => x.ExecutionContext != null && x.Progress >= skipBackwardsThreshold) .ObserveOn(schedulerService.MainScheduler); this.skipBackwardsCommand = ReactiveCommand .CreateAsyncObservable(canSkipBackwards, this.OnSkipBackwardsAsync, schedulerService.MainScheduler) .AddTo(this.disposables); var canSkipForwards = this .WhenAnyValue( x => x.ExecutionContext, x => x.CurrentExercise, (ec, currentExercise) => new { ExecutionContext = ec, CurrentExercise = currentExercise }) .Select(x => x.ExecutionContext != null && x.CurrentExercise != null && x.CurrentExercise != this.exercises.LastOrDefault()) .ObserveOn(schedulerService.MainScheduler); this.skipForwardsCommand = ReactiveCommand .CreateAsyncObservable(canSkipForwards, this.OnSkipForwardsAsync, schedulerService.MainScheduler) .AddTo(this.disposables); this.startCommand .CanExecuteObservable .Subscribe(x => this.IsStartVisible = x) .AddTo(this.disposables); this.pauseCommand .CanExecuteObservable .Subscribe(x => this.IsPauseVisible = x) .AddTo(this.disposables); this.resumeCommand .CanExecuteObservable .Subscribe(x => this.IsResumeVisible = x) .AddTo(this.disposables); this.startCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("start", ex)) .AddTo(this.disposables); this.pauseCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("pause", ex)) .AddTo(this.disposables); this.resumeCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("resume", ex)) .AddTo(this.disposables); this.skipBackwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip backwards", ex)) .AddTo(this.disposables); this.skipForwardsCommand .ThrownExceptions .Subscribe(ex => this.OnThrownException("skip forwards", ex)) .AddTo(this.disposables); // we don't use a reactive command here because switching in different commands causes it to get confused and // command binding leaves the target button disabled. We could also have not used command binding to get around // this problem this.playbackCommand = new PlaybackCommandImpl(this); // cancel the exercise program if the user navigates away this .hostScreen .Router .NavigationStack .ItemsRemoved .OfType<ExerciseProgramViewModel>() .SelectMany(x => x.StopAsync()) .Subscribe() .AddTo(this.disposables); }
public ExerciseProgramsViewModel( IAudioService audioService, IDelayService delayService, IExerciseDocumentService exerciseDocumentService, ILoggerService loggerService, ISchedulerService schedulerService, ISpeechService speechService, IStateService stateService, IScreen hostScreen, ExerciseProgramViewModelFactory exerciseProgramViewModelFactory) { audioService.AssertNotNull(nameof(audioService)); delayService.AssertNotNull(nameof(delayService)); exerciseDocumentService.AssertNotNull(nameof(exerciseDocumentService)); loggerService.AssertNotNull(nameof(loggerService)); schedulerService.AssertNotNull(nameof(schedulerService)); speechService.AssertNotNull(nameof(speechService)); stateService.AssertNotNull(nameof(stateService)); hostScreen.AssertNotNull(nameof(hostScreen)); exerciseProgramViewModelFactory.AssertNotNull(nameof(exerciseProgramViewModelFactory)); this.exerciseDocumentService = exerciseDocumentService; this.stateService = stateService; this.logger = loggerService.GetLogger(this.GetType()); this.hostScreen = hostScreen; this.disposables = new CompositeDisposable(); var documentsFromCache = this .stateService .GetAsync <string>(exerciseProgramsCacheKey) .Where(x => x != null) .Select(x => new DocumentSourceWith <string>(DocumentSource.Cache, x)); var documentsFromService = this .exerciseDocumentService .ExerciseDocument .Where(x => x != null) .Select(x => new DocumentSourceWith <string>(DocumentSource.Service, x)); var documents = documentsFromCache .Catch((Exception ex) => Observable.Empty <DocumentSourceWith <string> >()) .Concat(documentsFromService) .Do(x => this.logger.Debug("Received document from {0}.", x.Source)) .Publish(); var safeDocuments = documents .Catch((Exception ex) => Observable.Empty <DocumentSourceWith <string> >()); var results = documents .ObserveOn(schedulerService.TaskPoolScheduler) .Select( x => { IResult <ExercisePrograms> parsedExercisePrograms; using (this.logger.Perf("Parsing exercise programs from {0}.", x.Source)) { parsedExercisePrograms = ExercisePrograms.TryParse(x.Item, audioService, delayService, loggerService, speechService); } return(new DocumentSourceWith <IResult <ExercisePrograms> >(x.Source, parsedExercisePrograms)); }) .Publish(); var safeResults = results .Catch((Exception ex) => Observable.Empty <DocumentSourceWith <IResult <ExercisePrograms> > >()); safeResults .Select(x => x.Item.WasSuccessful ? null : x.Item.ToString()) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.ParseErrorMessage = x) .AddTo(this.disposables); results .Select(x => !x.Item.WasSuccessful ? ExerciseProgramsViewModelStatus.ParseFailed : x.Source == DocumentSource.Cache ? ExerciseProgramsViewModelStatus.LoadedFromCache : ExerciseProgramsViewModelStatus.LoadedFromService) .Catch((Exception ex) => Observable.Return(ExerciseProgramsViewModelStatus.LoadFailed)) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.Status = x) .AddTo(this.disposables); safeResults .Select(x => x.Item.WasSuccessful ? x.Item.Value : null) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.Model = x) .AddTo(this.disposables); this.WhenAnyValue(x => x.Model) .Select(x => x == null ? null : x.Programs.CreateDerivedCollection(y => exerciseProgramViewModelFactory(y))) .ObserveOn(schedulerService.MainScheduler) .Subscribe(x => this.Programs = x) .AddTo(this.disposables); safeDocuments .Where(x => x.Source == DocumentSource.Service) .SelectMany(x => this.stateService.SetAsync(exerciseProgramsCacheKey, x.Item)) .Subscribe() .AddTo(this.disposables); results .Connect() .AddTo(this.disposables); documents .Connect() .AddTo(this.disposables); this .WhenAnyValue(x => x.SelectedProgram) .Where(x => x != null) .Subscribe(x => this.hostScreen.Router.Navigate.Execute(x)) .AddTo(this.disposables); this .hostScreen .Router .CurrentViewModel .OfType <ExerciseProgramsViewModel>() .Subscribe(x => x.SelectedProgram = null) .AddTo(this.disposables); }