}
}
- public void NavigateToSummaryView(SummaryType summaryType, WorkoutViewModel workoutViewModel)
- {
- var view = new SummaryView()
- {
- BindingContext = new SummaryViewModel(summaryType, workoutViewModel),
- };
- navigation.Push(view);
- }
-
public void Pop()
{
navigation.Pop();
using Fitness.Models;
using Fitness.Services;
using Tizen.Multimedia.Vision;
+using Tizen.NUI;
using Tizen.NUI.Binding;
namespace Fitness.ViewModels
/// </summary>
public class ExercisingViewModel : ChangeWorkoutViewModel
{
+ private const string EndWorkoutSummaryTitle = "The current session will be stopped.";
+ private const string ChangeWorkoutSummaryTitle = "Do you want to change workout?";
+ private const string TryAgainSummaryTitle = "Do you want to try again?";
+ private const string TimeIsUpSummaryTitle = "Great job!";
+ private const string DefaultSummaryTitle = "Summary";
+
private WorkoutState state;
private int score;
private int hold;
private int repetitions;
private Landmark[,] poseLandmarks;
+ private bool isSummaryVisible;
+ private ICommand summaryOkCommand;
+ private string summaryTitle;
+ private bool isLoading;
/// <summary>
/// Initializes a new instance of the <see cref="ExercisingViewModel"/> class.
public ExercisingViewModel(WorkoutViewModel workoutViewModel)
{
PauseResumeWorkout = new Command(TriggerPauseResumeWorkout);
- TryAgain = new Command(ExecuteTryAgain);
- EndWorkout = new Command(ExecuteEndWorkout);
+ TryAgain = new Command(ConfirmTryAgain);
+ EndWorkout = new Command(ConfirmEndWorkout);
State = WorkoutState.Playing;
CurrentWorkout = workoutViewModel;
SquatService = new SquatService()
HoldTimeThreshold = 1200,
};
SquatService.ExerciseStateUpdated += SquatService_ExerciseStateUpdated;
+ SummaryBackCommand = new Command(ExecuteCloseSummary);
+ }
+
+ public ICommand SummaryBackCommand { get; private set; }
+
+ public ICommand SummaryOkCommand
+ {
+ get => summaryOkCommand;
+ private set
+ {
+ if (value != summaryOkCommand)
+ {
+ summaryOkCommand = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
+ public bool IsLoading
+ {
+ get => isLoading;
+ set
+ {
+ if (value != isLoading)
+ {
+ isLoading = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
+
+ public string SummaryTitle
+ {
+ get => summaryTitle;
+ private set
+ {
+ if (value != summaryTitle)
+ {
+ summaryTitle = value;
+ RaisePropertyChanged();
+ }
+ }
}
/// <summary>
}
/// <summary>
+ /// Gets AverageScore.
+ /// </summary>
+ public int AverageScore { get; private set; } = 98;
+
+ /// <summary>
+ /// Gets TotalCount.
+ /// </summary>
+ public int TotalCount { get; private set; } = 27;
+
+ /// <summary>
+ /// Repetitions made in current Workout
/// Gets the property specifying the correctness of the exercise - values ranging from 0 to 100.
/// </summary>
public int Score
}
}
+ /// <summary>
+ /// Gets TotalTime.
+ /// </summary>
+ public TimeSpan TotalTime { get; private set; } = new TimeSpan(0, 3, 39);
+
/// <summary>
/// Gets the TimeLeft in workout.
/// </summary>
public WorkoutState State
{
get => state;
- private set
+ set
{
if (state != value)
{
/// </summary>
public string PreviewVideoUrl { get; private set; }
- /// <summary>
- /// Gets Prev Command.
- /// </summary>
- public ICommand Prev { get; private set; }
-
- /// <summary>
- /// Gets Next Command.
- /// </summary>
- public ICommand Next { get; private set; }
+ public bool IsSummaryVisible
+ {
+ get => isSummaryVisible;
+ private set
+ {
+ if (value != isSummaryVisible)
+ {
+ isSummaryVisible = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
/// <summary>
/// Gets PauseWorkout Command.
public IExerciseService SquatService { get; private set; }
/// <inheritdoc />
- protected override void GoPrevious()
+ protected override void GoPrevious() => ConfirmChangeWorkout(-1);
+
+ /// <inheritdoc />
+ protected override void GoNext() => ConfirmChangeWorkout(1);
+
+ private void ConfirmChangeWorkout(int offset)
{
if (State == WorkoutState.Playing)
{
State = WorkoutState.Paused;
}
- Services.NavigationService.Instance.NavigateToSummaryView(SummaryType.ChangeToPreviousWorkout, CurrentWorkout);
+ SummaryOkCommand = new Command(() => { ExecuteChangeWorkout(offset); });
+ SummaryTitle = GetSummaryTitle(SummaryType.ChangeWorkout);
+ IsSummaryVisible = true;
}
- /// <inheritdoc />
- protected override void GoNext()
+ private void ExecuteCloseSummary()
{
- if (State == WorkoutState.Playing)
+ State = WorkoutState.Playing;
+ IsSummaryVisible = false;
+ }
+
+ private void ExecuteChangeWorkout(int offset = 0)
+ {
+ int idx = Workouts.IndexOf(CurrentWorkout) + offset;
+ if (idx >= 0 && idx < Workouts.Count)
{
- State = WorkoutState.Paused;
+ CurrentWorkout = Workouts[idx];
}
- Services.NavigationService.Instance.NavigateToSummaryView(SummaryType.ChangeToNextWorkout, CurrentWorkout);
+ StartNewWorkout();
+ }
+
+ private void StartNewWorkout()
+ {
+ State = WorkoutState.Playing;
+ IsSummaryVisible = false;
+ IsLoading = true;
+ }
+
+ private string GetSummaryTitle(SummaryType type)
+ {
+ switch (type)
+ {
+ case SummaryType.EndWorkout:
+ return EndWorkoutSummaryTitle;
+ case SummaryType.ChangeWorkout:
+ return ChangeWorkoutSummaryTitle;
+ case SummaryType.TryAgain:
+ return TryAgainSummaryTitle;
+ case SummaryType.TimeIsUp:
+ return TimeIsUpSummaryTitle;
+ default:
+ Services.Logger.Warn($"Unable to find summary title for type: {type}");
+ return DefaultSummaryTitle;
+ }
}
private void SquatService_ExerciseStateUpdated(object sender, ExerciseEventArgs e)
}
}
- private void ExecuteTryAgain()
+ private void ConfirmTryAgain()
{
- Services.NavigationService.Instance.NavigateToSummaryView(SummaryType.TryAgain, CurrentWorkout);
+ SummaryOkCommand = new Command(() => { ExecuteChangeWorkout(0); });
+ SummaryTitle = GetSummaryTitle(SummaryType.TryAgain);
+ IsSummaryVisible = true;
}
- private void ExecuteEndWorkout()
+ private void ConfirmEndWorkout()
{
- Services.NavigationService.Instance.NavigateToSummaryView(SummaryType.EndWorkout, CurrentWorkout);
+ SummaryOkCommand = new Command(Services.NavigationService.Instance.PopToRoot);
+ SummaryTitle = GetSummaryTitle(SummaryType.EndWorkout);
+ IsSummaryVisible = true;
}
}
}
}
}
- private void StartCounting()
+ public void StartCounting()
{
int count = 0;
timer = new Timer(TickIntervalInMilliseconds);
/// </summary>
EndWorkout,
- /// <summary>
- /// Change to the next workout.
- /// </summary>
- ChangeToNextWorkout,
-
/// <summary>
/// Change to the previous workout.
/// </summary>
- ChangeToPreviousWorkout,
+ ChangeWorkout,
/// <summary>
/// Try again.
+++ /dev/null
-using System;
-using System.Collections.Generic;
-using System.Windows.Input;
-using Fitness.Models;
-using Fitness.Services;
-using Tizen.NUI.Binding;
-
-namespace Fitness.ViewModels
-{
- /// <summary>
- /// SummaryViewModel class.
- /// </summary>
- public class SummaryViewModel : BaseViewModel
- {
- private const string EndWorkoutTitle = "The current session will be stopped.";
- private const string ChangeWorkoutTitle = "Do you want to change workout?";
- private const string TryAgainTitle = "Do you want to try again?";
- private const string TimeIsUpTitle = "Great job!";
-
- private SummaryType currentSummaryType;
- private string title;
- private Dictionary<SummaryType, string> summaryTitleMap;
- private ICommand ok;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SummaryViewModel"/> class.
- /// </summary>
- public SummaryViewModel(SummaryType summaryType, WorkoutViewModel workoutViewModel)
- {
- Back = new Command(() => { NavigationService.Instance.Pop(); });
- InitializeTitleMap();
- UpdateTitle();
- SetOkCommand();
- CurrentSummaryType = summaryType;
- CurrentWorkout = workoutViewModel;
- Workouts = WorkoutRepository.Instance.GetAll();
- }
-
- /// <summary>
- /// Gets back command.
- /// </summary>
- public ICommand Back { get; private set; }
-
- /// <summary>
- /// Gets Ok command.
- /// </summary>
- public ICommand Ok
- {
- get => ok;
- private set
- {
- if (ok != value)
- {
- ok = value;
- RaisePropertyChanged();
- }
- }
- }
-
- /// <summary>
- /// Gets or sets the current summary type.
- /// </summary>
- public SummaryType CurrentSummaryType
- {
- get => currentSummaryType;
- set
- {
- if (value != currentSummaryType)
- {
- currentSummaryType = value;
- RaisePropertyChanged();
- }
-
- UpdateTitle();
- SetOkCommand();
- }
- }
-
- /// <summary>
- /// Gets list of all available workouts.
- /// </summary>
- public IList<WorkoutViewModel> Workouts { get; private set; }
-
- /// <summary>
- /// Gets title.
- /// </summary>
- public string Title
- {
- get => title;
- private set
- {
- if (title != value)
- {
- title = value;
- RaisePropertyChanged();
- }
- }
- }
-
- /// <summary>
- /// Gets TotalTime.
- /// </summary>
- public TimeSpan TotalTime { get; private set; } = new TimeSpan(0, 3, 39);
-
- /// <summary>
- /// Gets TotalCount.
- /// </summary>
- public int TotalCount { get; private set; } = 27;
-
- /// <summary>
- /// Gets AverageScore.
- /// </summary>
- public int AverageScore { get; private set; } = 98;
-
- /// <summary>
- /// Gets current workout.
- /// </summary>
- public WorkoutViewModel CurrentWorkout { get; internal set; }
-
- private void InitializeTitleMap()
- {
- summaryTitleMap = new Dictionary<SummaryType, string>
- {
- { SummaryType.EndWorkout, EndWorkoutTitle },
- { SummaryType.ChangeToPreviousWorkout, ChangeWorkoutTitle },
- { SummaryType.ChangeToNextWorkout, ChangeWorkoutTitle },
- { SummaryType.TryAgain, TryAgainTitle },
- { SummaryType.TimeIsUp, TimeIsUpTitle },
- };
- }
-
- private void UpdateTitle()
- {
- if (summaryTitleMap.TryGetValue(currentSummaryType, out string title))
- {
- Title = title;
- }
- }
-
- private void SetOkCommand()
- {
- switch (currentSummaryType)
- {
- case SummaryType.ChangeToPreviousWorkout:
- Ok = new Command(() => { ChangeWorkout(-1); });
- break;
- case SummaryType.ChangeToNextWorkout:
- Ok = new Command(() => { ChangeWorkout(1); });
- break;
- case SummaryType.TryAgain:
- Ok = new Command(() => { ChangeWorkout(); });
- break;
- default:
- Ok = new Command(() =>
- {
- NavigationService.Instance.NavigateToMainView();
- });
- break;
- }
- }
-
- private void ChangeWorkout(int offset = 0)
- {
- WorkoutViewModel nextWorkout;
-
- int idx = Workouts.IndexOf(CurrentWorkout) + offset;
- if (idx >= 0 && idx < Workouts.Count)
- {
- nextWorkout = Workouts[idx];
- }
- else
- {
- nextWorkout = CurrentWorkout;
- }
-
- NavigationService.Instance.Pop();
- NavigationService.Instance.Pop();
- _ = NavigationService.Instance.NavigateToExercisingView(nextWorkout);
- }
- }
-}
true,
propertyChanged: OnIsPlayingChanged);
+ public static readonly BindableProperty IsLoadingProperty = BindableProperty.Create(
+ "IsLoading",
+ typeof(bool),
+ typeof(ExercisingView),
+ true,
+ propertyChanged: OnIsLoadingChanged);
+
private const int TransitionTime = 500;
private CancellationTokenSource source;
private bool isInitialized = false;
+ private bool isLoading = false;
private (Coordinates Preview, Coordinates Camera) playing;
private (Coordinates Preview, Coordinates Camera) pause =
(
}
}
+ private static void OnIsLoadingChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (newValue is bool isLoading && bindable is ExercisingView view)
+ {
+ view.isLoading = isLoading;
+
+ if (isLoading)
+ {
+ view.RestartLoading();
+ }
+ }
+ }
+
private static (Task, Task) Animate(View view, Coordinates from, Coordinates to, CancellationToken token)
{
Task Scale()
private void Initialize()
{
InitializeComponent();
+ LoadingView.Loaded += OnLoaded;
PlayingView.PreviewStub.Relayout += OnPlayingViewRelayout;
cameraView.Preview += DetectPreview;
PauseView.Hide();
PlayingView.Show();
- Preview.Play();
+ if (!isLoading)
+ {
+ Preview.Play();
+ }
}
else
{
{
UpdatePositionAndSize();
isInitialized = true;
+ }
- LoadingView.Loaded += OnLoaded;
+ private void RestartLoading()
+ {
+ Preview.Stop();
+ LoadingView.StartLoading();
}
private void OnLoaded(object sender, System.EventArgs e)
{
+ if (BindingContext is ExercisingViewModel viewModel)
+ {
+ viewModel.IsLoading = false;
+ }
+
Preview.Play();
}
using System;
using Fitness.Controls;
+using Fitness.ViewModels;
using Tizen.NUI;
using Tizen.NUI.BaseComponents;
Hide();
}
}
+
+ public void StartLoading()
+ {
+ if (BindingContext is LoadingViewModel viewModel)
+ {
+ Show();
+ viewModel.StartCounting();
+ }
+ }
}
}
xmlns:ctrl="clr-namespace:Fitness.Controls"
xmlns:views="clr-namespace:Fitness.Views"
xmlns:converters="clr-namespace:Fitness.Views.Converters"
+ xmlns:behaviors="clr-namespace:Fitness.Views.Behaviors"
x:Name="Root"
BackgroundColor="#EEEFF1"
- IsPlaying="{Binding State, Converter={x:Static converters:WorkoutStateToBoolConverter.Converter}}">
-
+ IsPlaying="{Binding State, Converter={x:Static converters:WorkoutStateToBoolConverter.Converter}}"
+ IsLoading="{Binding IsLoading}">
+
<views:PlayingView x:Name="PlayingView"
BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"/>
-
+
<!--Layer-->
<View x:Name="cameraOverlayView" PositionX="796" PositionY="152" SizeWidth="640" SizeHeight="480">
</View>
<!--Layer-->
-
+
<VideoView x:Name="Preview"
Underlay="False"
ResourceUrl="{Binding CurrentWorkout.VideoUrl}"
Looping="True"/>
-
+
<!--Layer-->
-
+
<views:PauseView x:Name="PauseView"
BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"/>
Title="{Binding CurrentWorkout.Title}"/>
<views:LoadingView x:Name="LoadingView"/>
-
+
+ <views:SummaryView x:Name="SummaryView"
+ behaviors:VisibilitySetter.IsVisible="{Binding IsSummaryVisible}"/>
+
</ctrl:Page>
<View Size="{views:SizeInUnits Height=10}"
HeightSpecification="{Static LayoutParamPolicies.MatchParent}"/>
-
+
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Text="{Binding Title}"
+ Text="{Binding SummaryTitle}"
PixelSize="40"
WidthSpecification="{Static LayoutParamPolicies.MatchParent}"
HeightSpecification="{Static LayoutParamPolicies.MatchParent}"
</View>
<View Size="{views:SizeInUnits Width=11}"/>
-
+
<TextLabel BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
Text="{Binding TotalTime, StringFormat='{0:m\\:ss}'}"
HorizontalAlignment="Begin"
</View.Layout>
<View Size="{views:SizeInUnits Width=20, Height=26}"/>
-
+
<ctrl:NinePatchButton Text="Back"
Size="{views:SizeInUnits Width=100, Height=26}"
BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Command="{Binding Back}"
+ Command="{Binding SummaryBackCommand}"
behaviors:StyleSetter.Style="{Binding Source={x:Static styles:Buttons.Outline}}"/>
<View Weight="1"
HeightSpecification="{Static LayoutParamPolicies.MatchParent}"
WidthSpecification="{Static LayoutParamPolicies.MatchParent}"/>
-
+
<ctrl:NinePatchButton Text="Ok"
Size="{views:SizeInUnits Width=100, Height=26}"
BindingContext="{Binding Source={x:Reference Root}, Path=BindingContext}"
- Command="{Binding Ok}"
+ Command="{Binding SummaryOkCommand}"
behaviors:StyleSetter.Style="{Binding Source={x:Static styles:Buttons.Inverse}}"/>
<View Size="{views:SizeInUnits Width=20, Height=24}"/>