Skip to content

Commit 48c3699

Browse files
Episodes 9 & 10: Working on the Summary Page and Page Model
Added new Labels for the Summary Page card titles and the custom renderers for the Hours Progress View.
1 parent 560f241 commit 48c3699

19 files changed

+348
-27
lines changed
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.timetrackertutorial">
3-
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
4-
<application android:label="TimeTrackerTutorial.Android"></application>
5-
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
6-
</manifest>
3+
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
4+
<application android:label="TimeTrackerTutorial.Android"></application>
5+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
6+
</manifest>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using Android.Content;
3+
using Android.Graphics;
4+
using TimeTrackerTutorial.Droid.Renderers;
5+
using TimeTrackerTutorial.Views;
6+
using Xamarin.Forms;
7+
using Xamarin.Forms.Platform.Android;
8+
using Color = Xamarin.Forms.Color;
9+
10+
[assembly: ExportRenderer(typeof(HoursProgressView), typeof(HoursProgressViewRenderer))]
11+
namespace TimeTrackerTutorial.Droid.Renderers
12+
{
13+
public class HoursProgressViewRenderer : ViewRenderer
14+
{
15+
private HoursProgressView _view;
16+
17+
public HoursProgressViewRenderer(Context context) : base(context)
18+
{
19+
SetWillNotDraw(false);
20+
}
21+
22+
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
23+
{
24+
base.OnElementChanged(e);
25+
_view = Element as HoursProgressView;
26+
}
27+
28+
protected override void OnDraw(Canvas canvas)
29+
{
30+
base.OnDraw(canvas);
31+
var paint = new Paint();
32+
paint.Color = Color.Gray.ToAndroid();
33+
paint.StrokeWidth = Context.ToPixels(5);
34+
canvas.DrawLine(0, canvas.Height / 2, canvas.Width, canvas.Height / 2, paint);
35+
36+
var currentProgressWidth = (_view.Current - _view.Min) / (_view.Max - _view.Min);
37+
paint.Color = Color.Blue.ToAndroid();
38+
canvas.DrawLine(0, canvas.Height / 2, (float)(canvas.Width * currentProgressWidth), canvas.Height / 2, paint);
39+
}
40+
}
41+
}

TimeTrackerTutorial.Android/TimeTrackerTutorial.Android.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
1717
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
1818
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
19-
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
2019
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
2120
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
2221
<AndroidUseAapt2>true</AndroidUseAapt2>
@@ -61,6 +60,7 @@
6160
<Compile Include="MainActivity.cs" />
6261
<Compile Include="Resources\Resource.designer.cs" />
6362
<Compile Include="Properties\AssemblyInfo.cs" />
63+
<Compile Include="Renderers\HoursProgressViewRenderer.cs" />
6464
</ItemGroup>
6565
<ItemGroup>
6666
<None Include="Resources\AboutResources.txt" />
@@ -87,10 +87,11 @@
8787
</ItemGroup>
8888
<ItemGroup>
8989
<Folder Include="Resources\drawable\" />
90+
<Folder Include="Renderers\" />
9091
</ItemGroup>
9192
<ItemGroup>
9293
<ProjectReference Include="..\TimeTrackerTutorial\TimeTrackerTutorial.csproj">
93-
<Project>{4180C9FA-6AB4-40FF-9EC0-7EBCF63CD768}</Project>
94+
<Project>{764C33A9-C9F3-4D94-B945-3F2168473335}</Project>
9495
<Name>TimeTrackerTutorial</Name>
9596
</ProjectReference>
9697
</ItemGroup>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using CoreAnimation;
3+
using TimeTrackerTutorial.iOS.Renderers;
4+
using TimeTrackerTutorial.Views;
5+
using UIKit;
6+
using Xamarin.Forms;
7+
using Xamarin.Forms.Platform.iOS;
8+
9+
[assembly: ExportRenderer(typeof(HoursProgressView), typeof(HoursProgressViewRenderer))]
10+
namespace TimeTrackerTutorial.iOS.Renderers
11+
{
12+
public class HoursProgressViewRenderer : ViewRenderer
13+
{
14+
private CAShapeLayer _backgroundLayer;
15+
private CAShapeLayer _foregroundLayer;
16+
17+
public HoursProgressViewRenderer()
18+
{
19+
}
20+
21+
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
22+
{
23+
base.OnElementChanged(e);
24+
if (e.NewElement != null)
25+
{
26+
e.NewElement.SizeChanged += NewElement_SizeChanged;
27+
}
28+
29+
if (e.OldElement != null)
30+
{
31+
e.OldElement.SizeChanged -= NewElement_SizeChanged;
32+
}
33+
}
34+
35+
private void NewElement_SizeChanged(object sender, EventArgs e)
36+
{
37+
if (_backgroundLayer != null)
38+
{
39+
_backgroundLayer.RemoveFromSuperLayer();
40+
}
41+
42+
if (_foregroundLayer != null)
43+
{
44+
_foregroundLayer.RemoveFromSuperLayer();
45+
}
46+
47+
var view = Element as HoursProgressView;
48+
var backgroundPath = new UIBezierPath();
49+
backgroundPath.MoveTo(new CoreGraphics.CGPoint(0, view.Height / 2));
50+
backgroundPath.AddLineTo(new CoreGraphics.CGPoint(view.Width, view.Height / 2));
51+
backgroundPath.LineWidth = 5;
52+
_backgroundLayer = new CAShapeLayer();
53+
_backgroundLayer.Path = backgroundPath.CGPath;
54+
_backgroundLayer.StrokeColor = UIColor.Gray.CGColor;
55+
56+
Layer.AddSublayer(_backgroundLayer);
57+
58+
var currentProgressWidth = (view.Current - view.Min) / (view.Max - view.Min);
59+
var foregroundPath = new UIBezierPath();
60+
foregroundPath.MoveTo(new CoreGraphics.CGPoint(0, view.Height / 2));
61+
foregroundPath.AddLineTo(new CoreGraphics.CGPoint(view.Width * currentProgressWidth, view.Height / 2));
62+
63+
_foregroundLayer = new CAShapeLayer();
64+
foregroundPath.LineWidth = 5;
65+
_foregroundLayer.Path = foregroundPath.CGPath;
66+
_foregroundLayer.StrokeColor = UIColor.Blue.CGColor;
67+
68+
Layer.AddSublayer(_foregroundLayer);
69+
70+
}
71+
}
72+
}

TimeTrackerTutorial.iOS/TimeTrackerTutorial.iOS.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<None Include="Entitlements.plist" />
6969
<None Include="Info.plist" />
7070
<Compile Include="Properties\AssemblyInfo.cs" />
71+
<Compile Include="Renderers\HoursProgressViewRenderer.cs" />
7172
</ItemGroup>
7273
<ItemGroup>
7374
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
@@ -129,8 +130,11 @@
129130
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
130131
<ItemGroup>
131132
<ProjectReference Include="..\TimeTrackerTutorial\TimeTrackerTutorial.csproj">
132-
<Project>{4180C9FA-6AB4-40FF-9EC0-7EBCF63CD768}</Project>
133+
<Project>{764C33A9-C9F3-4D94-B945-3F2168473335}</Project>
133134
<Name>TimeTrackerTutorial</Name>
134135
</ProjectReference>
135136
</ItemGroup>
137+
<ItemGroup>
138+
<Folder Include="Renderers\" />
139+
</ItemGroup>
136140
</Project>

TimeTrackerTutorial/PageModels/Base/PageModelLocator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using TimeTrackerTutorial.Services.Account;
66
using TimeTrackerTutorial.Services.Navigation;
77
using TimeTrackerTutorial.Services.Statement;
8+
using TimeTrackerTutorial.Services.Work;
89
using TinyIoC;
910
using Xamarin.Forms;
1011

@@ -28,10 +29,11 @@ static PageModelLocator()
2829
Register<SummaryPageModel, SummaryPage>();
2930
Register<TimeClockPageModel, TimeClockPage>();
3031

31-
// Register Services (registerd as Singletons by default)
32+
// Register Services (registered as Singletons by default)
3233
_container.Register<INavigationService, NavigationService>();
3334
_container.Register<IAccountService, MockAccountService>();
3435
_container.Register<IStatementService, MockStatementService>();
36+
_container.Register<IWorkService, MockWorkService>();
3537
}
3638

3739
/// <summary>

TimeTrackerTutorial/PageModels/SummaryPageModel.cs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Text;
45
using System.Threading.Tasks;
56
using TimeTrackerTutorial.Models;
67
using TimeTrackerTutorial.PageModels.Base;
8+
using TimeTrackerTutorial.Services.Account;
79
using TimeTrackerTutorial.Services.Statement;
10+
using TimeTrackerTutorial.Services.Work;
11+
using TimeTrackerTutorial.ViewModels;
812

913
namespace TimeTrackerTutorial.PageModels
1014
{
@@ -31,25 +35,66 @@ public DateTime CurrentPeriodPayDate
3135
set => SetProperty(ref _currentPeriodPayDate, value);
3236
}
3337

34-
private List<PayStatement> _statements;
35-
public List<PayStatement> Statements
38+
private List<PayStatementViewModel> _statements;
39+
public List<PayStatementViewModel> Statements
3640
{
3741
get => _statements;
3842
set => SetProperty(ref _statements, value);
3943
}
4044

45+
private IAccountService _accountService;
4146
private IStatementService _statementService;
47+
private IWorkService _workService;
48+
private double _hourlyRate;
4249

43-
public SummaryPageModel(IStatementService statementService)
50+
public SummaryPageModel(IStatementService statementService, IWorkService workService,
51+
IAccountService accountService)
4452
{
53+
_accountService = accountService;
4554
_statementService = statementService;
55+
_workService = workService;
4656
}
4757

4858
public override async Task InitializeAsync(object navigationData)
4959
{
50-
Statements = await _statementService.GetStatementHistoryAsync();
51-
60+
_hourlyRate = await _accountService.GetCurrentPayRateAsync();
61+
var statements = await _statementService.GetStatementHistoryAsync();
62+
if (statements != null)
63+
{
64+
Statements = statements.Select(s => new PayStatementViewModel(s)).ToList();
65+
var lastStatement = statements.FirstOrDefault();
66+
if (lastStatement != null)
67+
{
68+
var today = DateTime.Now;
69+
var max = 100;
70+
var currentCount = 0;
71+
var currentEnd = lastStatement.End;
72+
while (currentEnd < today && currentCount < max)
73+
{
74+
currentEnd = currentEnd.AddDays(14);
75+
++currentCount;
76+
}
77+
if (currentEnd > today)
78+
{
79+
if (currentEnd.AddDays(-13) < today)
80+
{
81+
SetDateRange(currentEnd.AddDays(-13), currentEnd);
82+
}
83+
}
84+
}
85+
}
86+
var currentPeriodItems = await _workService.GetWorkForThisPeriodAsync();
87+
foreach (var item in currentPeriodItems)
88+
{
89+
CurrentPeriodEarnings += item.Total.TotalHours * _hourlyRate;
90+
}
5291
await base.InitializeAsync(navigationData);
5392
}
93+
94+
private void SetDateRange(DateTime start, DateTime end)
95+
{
96+
CurrentPayDateRange = start.ToString("MMMM d") + " - " + end.ToString("MMMM d, yyyy");
97+
CurrentPeriodPayDate = end.AddDays(6);
98+
}
5499
}
55100
}

TimeTrackerTutorial/Pages/SummaryPage.xaml

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
44
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
55
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6-
mc:Ignorable="d" x:Class="TimeTrackerTutorial.Pages.SummaryPage">
6+
xmlns:views="clr-namespace:TimeTrackerTutorial.Views" mc:Ignorable="d"
7+
xmlns:labels="clr-namespace:TimeTrackerTutorial.Views.Labels"
8+
x:Class="TimeTrackerTutorial.Pages.SummaryPage">
79
<ContentPage.Content>
8-
<StackLayout>
10+
<StackLayout Margin="10">
911
<!-- This Pay Period Overview -->
1012
<Frame>
1113
<StackLayout>
12-
<Label Text="Current Pay Period Estimate" />
14+
<labels:TitleLabel Text="Current Pay Period Estimate" />
1315
<Label Text="{Binding CurrentPayDateRange}" />
1416
<Label Text="{Binding CurrentPeriodEarnings, StringFormat='{}{0:C}'}" />
1517
</StackLayout>
@@ -18,25 +20,20 @@
1820
<!-- Next Pay Date -->
1921
<Frame>
2022
<StackLayout>
21-
<Label Text="Payment Date for this Period" />
23+
<labels:TitleLabel Text="Payment Date for this Period" />
2224
<Label Text="{Binding CurrentPeriodPayDate, StringFormat='{}{0:MMMM d, yyyy}'}" />
2325
</StackLayout>
2426
</Frame>
2527

2628
<!-- Previous Pay Statements -->
2729
<Frame>
2830
<StackLayout>
29-
<Label Text="Pay Statements" />
31+
<labels:TitleLabel Text="Pay Statements" />
3032
<ListView ItemsSource="{Binding Statements}">
3133
<ListView.ItemTemplate>
3234
<DataTemplate>
3335
<ViewCell>
34-
<StackLayout Orientation="Horizontal">
35-
<Label Text="{Binding Date}"
36-
HorizontalOptions="StartAndExpand" />
37-
<Label Text="{Binding Earnings}"
38-
HorizontalOptions="End" />
39-
</StackLayout>
36+
<views:PayStatementView />
4037
</ViewCell>
4138
</DataTemplate>
4239
</ListView.ItemTemplate>

TimeTrackerTutorial/Services/Statement/MockStatementService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Threading.Tasks;
45
using TimeTrackerTutorial.Models;
56

@@ -19,7 +20,7 @@ public MockStatementService()
1920
Amount = 10,
2021
Date = DateTime.Parse("06/12/2020"),
2122
Start = DateTime.Parse("05/24/2020"),
22-
End = DateTime.Parse("06/04/2020"),
23+
End = DateTime.Parse("06/06/2020"),
2324
WorkItems = new List<WorkItem>
2425
{
2526
new WorkItem
@@ -34,7 +35,7 @@ public MockStatementService()
3435

3536
public Task<List<PayStatement>> GetStatementHistoryAsync()
3637
{
37-
return Task.FromResult(_items);
38+
return Task.FromResult(_items.OrderByDescending(s => s.Start).ToList());
3839
}
3940
}
4041
}

TimeTrackerTutorial/Services/Work/IWorkService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Collections.ObjectModel;
34
using System.Threading.Tasks;
45
using TimeTrackerTutorial.Models;
@@ -9,5 +10,6 @@ public interface IWorkService
910
{
1011
Task<bool> LogWorkAsync(WorkItem item);
1112
Task<ObservableCollection<WorkItem>> GetTodaysWorkAsync();
13+
Task<List<WorkItem>> GetWorkForThisPeriodAsync();
1214
}
1315
}

TimeTrackerTutorial/Services/Work/MockWorkService.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,22 @@ public Task<ObservableCollection<WorkItem>> GetTodaysWorkAsync()
2424
{
2525
return Task.FromResult(new ObservableCollection<WorkItem>(Items));
2626
}
27+
28+
public Task<List<WorkItem>> GetWorkForThisPeriodAsync()
29+
{
30+
return Task.FromResult(new List<WorkItem>
31+
{
32+
new WorkItem
33+
{
34+
Start = DateTime.Now.AddDays(-2),
35+
End = DateTime.Now.AddDays(-2).AddHours(1)
36+
},
37+
new WorkItem
38+
{
39+
Start = DateTime.Now.AddDays(-1),
40+
End = DateTime.Now.AddDays(-1).AddHours(1)
41+
},
42+
});
43+
}
2744
}
2845
}

0 commit comments

Comments
 (0)