Spread the love

You’ll have seen we’ve taken a look at .Net 5 Use in WPF before. Today we will delve into the design and implementation lifecycle I use for my work projects, for instance CommPlete for Even Grand Trading 141 cc.

Please note, this is experimental at best. We use Microsoft.EntityFrameworkCore.Sqlite in our App.

The focus of the blog post will be on my personal fitness app, EdgeHelper. I use it for other reasons in its original form, for the post and development cycle we will stick to fitness. This became the proof of concept for my future projects which I will use some new features in.

Update 2021/01/27: downloads updated below, note VS2019 16.8.4 seems to lock and crash on minimal background processes when you unrar and open the project. I’ve had the error occur and stop build and deploy, just force it to stop and it will work. Make your icons better if you use this for yourself, please, I need to make it prettier. The apk has a bug fix, the previous apk/code wont be able to use what I shared as ‘copy’.

Note: Please see edits at the end that are needed bugfixes.

The shared code here is just to discuss how it was implemented so you can see how we can get access to the new features. There are some broken ideas I’d love to here feedback on, such as putting records into the EntityFramework.

The concept behind the original EdgeHelper was to make an Android app for myself to hold several amounts of data I’d prefer to keep in one place. Namely,

  • Exercises
    The future concept originally in EdgeRsize moved here.
  • To Do
    I wanted one place to list all my To Do ideas in order of dates.
  • Recipes
    In a step to a new direction, I want to track the recipes I use, adjusted of course, for myself to use the adjustments again.
  • Carb Data
    I used open source nutritional data to track my calories for a while. It didn’t have too much purpose, I just wanted to see what I was eating.
Original EdgeHelper

While the above image shows more, we will focus on only exercises as our data for today. You should always approach projects in a way that’s manageable for you to move forwards. You don’t need to complete everything at once, don’t let the huge commit worry you, I just felt it would be useful to show off the cycle instead of hundreds of commits while I decided on what went into the views.

The original EdgeHelper APK was over 100mb in size, it was built right when C# 9.0 came out, I used random packages as needed. This time I paid careful attention and with the exercises alone the full package is 17.7mb total. Don’t cut corners and use the first packages that can give you whatever you’d need when you want to be able to distribute your APK.

I’ll try share this open-source code APK through f-droid when I’ve finished adding all the features I want.

Step 1: Create The Project

Mobile App (Xamarin.Forms)

There are many other concepts we could look at here, we’ll stick to keeping it minimal for ourselves. Create a folder for yourself, use git init, add Visual Studios .gitignore, put your project EdgeHelper into that directory. Keep your use of git up to date yourself, you can see the history in the download of the repo I used below.

I’m using VS2019 16.8.4.

Blank Android App

Once you’ve created the blank app in your repo you need to do a tiny change. Using a text editor, inside EdgeHelper.Android.csproj, and EdgeHelper.csproj, we add the property:

  <PropertyGroup>
    <LangVersion>9.0</LangVersion>
  </PropertyGroup>
I added it at the end of the last PropertyGroup

It adds other properties and files, but you should be able to adjust MainPage.xaml.cs to take a look if you did the right changes.

using Xamarin.Forms;

namespace EdgeHelper
{
    public record Monster(string Name, string Location);

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            var monster = new Monster("Joe", "Soap City");
            var name = monster.Name;
        }
    }
}

You can now explore C# 9.0 in your Android application project. Always double check it compiles and runs, there are occasions where you might need to relaunch the Android Emulator, or VS2019, as well.

The Android device I use in emulator is a Pixel 2 Pie 9.0 – API 28, my personal phone is a Samsung Galaxy A31. I’ve made sure the final product works on my personal phone perfectly.

I’m, quite obviously, not a graphic designer. The choices made for the UI were purely personal, I prefer the dark theme. You’ll remember in NaNoE.V2 I gave people the option for dark or light theme, it may eventually come here, it would be the same process with only slight adjustments.

The APK size at the end of this step was 30mb odd on the emulator.

Step 2: Build the MVVM and Add the DB

As an important note, these steps we use here are the general steps taken through the implementation. Code might be seen from later commits when you go through each commit in the repo for yourself.

I decided to use some new features of C# 9.0 here. For the models you’ll see we have two Records, that is MExercise and MExerciseSet, it adds a little complexity if we’d like to edit them. Just note the small, and clean information for data:

using System.ComponentModel.DataAnnotations.Schema;

namespace EdgeHelper.Models
{
    public record MExercise
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }
        public string Name { get; init; }
        public string Description { get; init; }
        public bool AllowRests { get; init; }

        public override string ToString() => Name;
    }
}
using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace EdgeHelper.Models
{
    public record MExerciseRecord
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public string TrackingAndNotes { get; set; }
        public string Exercise { get; set; }

        public override string ToString()
        {
            return StartTime.ToString("yyyy.MM.dd") + " - " + Exercise;
        }
    }
}

Now, we won’t dabble too much in the database binding here, yet. This isn’t as simple as we’d like the records to go, consider this:

    public record MExerciseSet(int ID, int ExerciseID, string Name, string Description, bool ShowTime, int Reps)
    {
        public override string ToString() => Reps + " x " + Name;
    }

Or perhaps this:

    public record MExerciseSet(int ID, int ExerciseID, string Name, string Description, bool ShowTime, int Reps)
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }

        public override string ToString() => Reps + " x " + Name;
    }

When using records you’d expect it to be way smaller, like above in Example 1, the use of records is quite fantastic for us. I found, like in Example 2, you’d need to mark the ID as the key. This never worked, it kept the ID as 0 when inserted to the DB. It added troubles where the data that came out the database would revert to showing the ID as 0 since the record shows 0, but the inner ID showed as the actual value in the DB.

The values worked somewhat better as a property on said record. Take note ID can be changed, however the other properties use the init creation. This might not be the exact idea behind how records are used, I just built it as close as I could to be usable for this example.

We then add the class for MExerciseRecord for ourselves.

using System;
using System.ComponentModel.DataAnnotations.Schema;

namespace EdgeHelper.Models
{
    public class MExerciseRecord
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public string TrackingAndNotes { get; set; }
        public string Exercise { get; set; }

        public override string ToString()
        {
            return StartTime.ToString("yyyy.MM.dd") + " - " + Exercise;
        }
    }
}

The reason to note make another record is perhaps confusing, it’s just to allow us to slowly increment the TrackingAndNotes while we do exercises. We could perhaps use the view model to store the values and also use a record for this, I just chose not to, for some arbitrary reason. I figured it would be nice to mix up data types for models to add value to the experiments.

Then, as you’d note, we add the nuget package Microsoft.EntityFramework.Sqlite. We add the simple context code for ourselves. We also move the Target Framework to .Net Standard 2.1.

using EdgeHelper.Models;
using Microsoft.EntityFrameworkCore;

namespace EdgeHelper
{
    public class DatabaseContext : DbContext
    {
        public DbSet<MExercise> Exercises { get; set; }
        public DbSet<MExerciseSet> ExerciseSets { get; set; }
        public DbSet<MExerciseRecord> ExerciseRecords { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder options)
            => options.UseSqlite("Data Source=" +
                                 System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + 
                                 "/edgehelper.db");
    }
}

As you can see, it’s such a simple method for us to implement our connection to our DB. Now, take note, the implementation and design choices up till now had a very simple point: keep the code understandable, and minimal, as much as possible.

We add a small class to add sample data if it’s empty of any exercises, this is meant to show it’s usable. Then we adjust MainPage.xaml.cs.

using EdgeHelper.Models;
using EdgeHelper.Views;
using System.Linq;
using Xamarin.Forms;

namespace EdgeHelper
{
    public partial class MainPage : ContentPage
    {
        public static DatabaseContext DB { get; private set; }

        public MainPage()
        {
            InitializeComponent();

            DB = new DatabaseContext();
            DB.Database.EnsureCreatedAsync();

            if (DB.Exercises.Count() == 0)
            {
                DbGenerator.Generate();
                DB.SaveChanges();
            }
            
            Navigator.Instance.SetViewport(frmContent);
            Navigator.Instance.GoTo("main");
        }
    }
}

As a note, the APK was 42.69mb at this point. You’ll see in Notes.txt we didn’t clean out unused references, and the likes.

For our ViewModels we add a small structure for ourselves, it’s for a simple idea. Let’s allow the view models to clean memory use a little, so to speak.

namespace EdgeHelper.ViewModels
{
    public interface IRefresh
    {
        public void Refreshed();
        public void Closed();
    }
}

While it’s perhaps confusing at the moment, take a look at AddExerciseViewModel for a little.

using Android.Widget;
using EdgeHelper.Models;
using EdgeHelper.Views;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;

namespace EdgeHelper.ViewModels
{
    class AddExerciseViewModel : IRefresh, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void Refreshed()
        {
            Name = "";
            Description = "";
            AllowRests = true;
            Sets = new ObservableCollection<MExerciseSet>();
            SetName = "";
            SetDescription = "";
            SetShowTime = false;
            SetReps = 0;
            _i = 0;

            if (PropertyChanged is not null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Description"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("AllowRests"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Sets"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("SetName"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("SetDescription"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("SetShowTime"));
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("SetReps"));
            }
        }

        public void Closed()
        {
            Name = null;
            Description = null;
            Sets = null;
            Selected = null;
            SetName = null;
            SetDescription = null;
        }

        private int _i { get; set; }

        public string Name { get; set; }
        public string Description { get; set; }
        public bool AllowRests { get; set; }
        public ObservableCollection<MExerciseSet> Sets { get; set; }

        public MExerciseSet Selected { get; set; }
        public string SetName { get; set; }
        public string SetDescription { get; set; }
        public bool SetShowTime { get; set; }
        public int SetReps { get; set; }

        public Command GoBack { get; init; }
        public Command GoAdd { get; init; }
        public Command MoveUp { get; init; }
        public Command MoveDown { get; init; }
        public Command AddSet { get; init; }
        public Command RemoveSet { get; init; }

        public AddExerciseViewModel()
        {
            GoBack = new Command(() =>
            {
                Navigator.Instance.GoTo("exercises");
            });

            GoAdd = new Command(() =>
            {
                if (MainPage.DB.Exercises.Where(a => a.Name == this.Name).FirstOrDefault() is not null)
                {
                    Toast.MakeText(Android.App.Application.Context, "You cant add an exercise with the same name...", ToastLength.Long).Show();
                }
                else if ((Name.Length < 1) || (Description.Length < 1) || (Sets.Count < 1))
                {
                    Toast.MakeText(Android.App.Application.Context, "We can't add an exercise that doesn't have all things needed in exercises", ToastLength.Long).Show();
                }
                else
                {
                    var db = MainPage.DB;
                    var exercise = new MExercise() { Name = this.Name, Description = this.Description, AllowRests = this.AllowRests };
                    db.Add(exercise);
                    db.SaveChanges();

                    var ex_id = db.Exercises
                                  .Where(a => ((a.Name == this.Name) && (a.Description == this.Description) && (a.AllowRests == this.AllowRests)))
                                  .First().ID;

                    foreach (var set in Sets)
                    {
                        set.ID = 0;
                        set.ExerciseID = ex_id;
                        db.Add(set);
                    }

                    db.SaveChanges();
                    Navigator.Instance.GoTo("exercises");
                }
            });

            MoveUp = new Command(() =>
            {
                if (Selected is not null)
                {
                    var pos = Sets.IndexOf(Selected);
                    if (pos > 0) Sets.Move(pos, pos - 1);
                }
            });

            MoveDown = new Command(() =>
            {
                if (Selected is not null)
                {
                    var pos = Sets.IndexOf(Selected);
                    if (pos < Sets.Count - 1) Sets.Move(pos, pos + 1);
                }
            });

            AddSet = new Command(() =>
            {
                if ((SetName.Length < 1) || (SetDescription.Length < 1))
                {
                    Toast.MakeText(Android.App.Application.Context, "We can't add a set to this exercise that doesn't have all fields", ToastLength.Long).Show();
                }
                else
                {
                    Sets.Add(new MExerciseSet() { ID = ++_i, Name = SetName, Description = SetDescription, ShowTime = SetShowTime, Reps = SetReps });
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Sets"));
                }
            });

            RemoveSet = new Command(() =>
            {
                if (Selected is not null)
                {
                    Sets.Remove(Selected);
                }
            });
        }
    }
}

You can no doubt tell, we refresh the data shown to be blank in the interface shown to the user here. When we leave the view it will be told it’s Closed and clear used memory a touch.

You can also see line 77 we create the Command to add this exercise into the database. Simple safety checks to see we don’t duplicate what we want unique in the data set.

Exercise History

You’ll also see I created a simple text based method to share Exercises to others. Consider you add an exercise you see in the latest fitness magazine you’re reading, you can add it for yourself, and just copy-paste it to send to your loved one that asks for it. This

The files are available in the download below, we then flesh out the usability ideas I have for how it all works. This is where similarly all the Views and ViewModels get fleshed out, if you have questions about what you see you can let me know.

Step 3: Improve the View

Before release you’d find it a good habit to adjust and improve the view the user would experience.

You’d note, you’d add a loading screen for yourself. This has a few components that are minimal to implement.

<?xml version="1.0" encoding="utf-8" ?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
    <bitmap android:src="@drawable/load" android:tileMode="disabled" android:gravity="fill" />
  </item>
</layer-list>

I also created EdgeHelper.Android/Resources/drawable/load.png. Yes, a simple image I threw in.

using Android.App;
using Android.Content;
using Android.OS;
using Android.Support.V7.App;
using System;

namespace EdgeHelper.Droid
{
    [Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
    public class SplashActivity : AppCompatActivity
    {
        public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
        {
            base.OnCreate(savedInstanceState, persistentState);
        }

        protected override void OnResume()
        {
            try
            {
                base.OnResume();
                var intent = new Intent(this, typeof(MainActivity));
                intent.AddFlags(ActivityFlags.NoAnimation);
                if (Intent.Extras != null)
                {
                    intent.PutExtras(Intent.Extras);
                }
                StartActivity(intent);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
            }
        }
    }
}

For MainActivity.cs we adjust the Activity.

    [Activity(Label = "EdgeHelper", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = false, NoHistory = false, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]

We adjust Styles.xml for ourselves.

<?xml version="1.0" encoding="utf-8" ?>
<resources>

  <style name="MainTheme" parent="MainTheme.Base">
    <!-- As of Xamarin.Forms 4.6 the theme has moved into the Forms binary -->
    <!-- If you want to override anything you can do that here. -->
    <!-- Underneath are a couple of entries to get you started. -->

    <!-- Set theme colors from https://aka.ms/material-colors -->
    <!-- colorPrimary is used for the default action bar background -->
    <!--<item name="colorPrimary">#2196F3</item>-->
    <!-- colorPrimaryDark is used for the status bar -->
    <!--<item name="colorPrimaryDark">#1976D2</item>-->
    <!-- colorAccent is used as the default value for colorControlActivated
         which is used to tint widgets -->
    <!--<item name="colorAccent">#FF4081</item>-->
  </style>
  <style name="MyTheme.Splash" parent="Theme.AppCompat.Light">
    <item name="android:windowBackground">@drawable/splash_screen</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowAnimationStyle">@null</item>
  </style>
</resources>

You can see it’s quite a simple idea, make the app look user friendly for the end user. Sure, it’s my personal design and style, you can take the liberty to make it look even better than the shared code. I’m stuck in the developer cycle of design and prefer dark themes.

Note: If you experience problems with xaml Frame components showing a white border you need to also set the BackgroundColor to what you’d prefer instead of the default colour scheme.

Step 4: My Exercises

Below are the imports needed if you’d like to get my full, revised from here, exercise sets to add to your own compiled version to try out. You can import them, seperately, within the test APK.

[Any] Jog|Job on Treadmill|0ﻼ
Jog on Treadmill|Try get 20minutes or more.|1|1ﻼ
[Any] Pullups|Pullups in door w/ bar|1ﻼ
Pullup|Slow up, Slow down|10|0ﻼ
Pullup|Slow up, Slow down|10|0ﻼ
Pullup|Slow up, Slow down|10|0ﻼ
Pullup|Slow up, Slow down|10|0ﻼ
[Any] Pushup + Situp|Minimal|1ﻼ
Situp|Slow up, Slow down. Hold for a second or two at top.|20|0ﻼ
Pushup|Slow up, Slow down|20|0ﻼ
Situp|Slow up, Slow down. Hold for a second or two at top.|20|0ﻼ
Pushup|Slow up, Slow down|20|0ﻼ
Pushup|Slow up, Slow down|20|0ﻼ
Situp|Slow up, Slow down. Hold for a second or two at top.|20|0ﻼ
[Everyday] Plank|Strengthen Spine|0ﻼ
Plank|Try go 60s or more|1|1ﻼ
[Monday] Legs and Arms|Weights needed|1ﻼ
Leg Extensions|Hold weight between feet, lie on your back feet extended out (e.g. on a bed). Lower and raise slow each count.|10|0ﻼ
Leg Extensions|Hold weight between feet, lie on your back feet extended out (e.g. on a bed). Lower and raise slow each count.|10|0ﻼ
Leg Extensions|Hold weight between feet, lie on your back feet extended out (e.g. on a bed). Lower and raise slow each count.|10|0ﻼ
Crunches|Lie down, hands join behind neck. Lift your back up to crunch your abs, hold a moment, release.|10|0ﻼ
Crunches|Lie down, hands join behind neck. Lift your back up to crunch your abs, hold a moment, release.|10|0ﻼ
Crunches|Lie down, hands join behind neck. Lift your back up to crunch your abs, hold a moment, release.|10|0ﻼ
Lunges|Step forward, one leg at a time. Go slow down forwards untill the knee is 90 degrees. Up again. Then use other leg, both need rep count.|10|0ﻼ
Lunges|Step forward, one leg at a time. Go slow down forwards untill the knee is 90 degrees. Up again. Then use other leg, both need rep count.|10|0ﻼ
Lunges|Step forward, one leg at a time. Go slow down forwards untill the knee is 90 degrees. Up again. Then use other leg, both need rep count.|10|0ﻼ
Lunges|Step forward, one leg at a time. Go slow down forwards untill the knee is 90 degrees. Up again. Then use other leg, both need rep count.|10|0ﻼ
Manual Hamstring Curls|Put max weight on each foot while you stand on knees. Lean half down forwards, hold a second, then move up again in slow motion.|10|0ﻼ
Manual Hamstring Curls|Put max weight on each foot while you stand on knees. Lean half down forwards, hold a second, then move up again in slow motion.|10|0ﻼ
Manual Hamstring Curls|Put max weight on each foot while you stand on knees. Lean half down forwards, hold a second, then move up again in slow motion.|10|0ﻼ
Manual Hamstring Curls|Put max weight on each foot while you stand on knees. Lean half down forwards, hold a second, then move up again in slow motion.|10|0ﻼ
Leg Extensions|Hold weight between feet, lie on your back feet extended out (e.g. on a bed). Lower and raise slow each count.|10|0ﻼ
Leg Extensions|Hold weight between feet, lie on your back feet extended out (e.g. on a bed). Lower and raise slow each count.|10|0ﻼ
Leg Extensions|Hold weight between feet, lie on your back feet extended out (e.g. on a bed). Lower and raise slow each count.|10|0ﻼ
Squat|Hold weights in hands, feet shoulder width apart. Slow down as far as you can go, slow up.|10|0ﻼ
Squat|Hold weights in hands, feet shoulder width apart. Slow down as far as you can go, slow up.|10|0ﻼ
Squat|Hold weights in hands, feet shoulder width apart. Slow down as far as you can go, slow up.|10|0ﻼ
Squat|Hold weights in hands, feet shoulder width apart. Slow down as far as you can go, slow up.|10|0ﻼ
Squat|Hold weights in hands, feet shoulder width apart. Slow down as far as you can go, slow up.|10|0ﻼ
Crunches|Lie down, hands join behind neck. Lift your back up to crunch your abs, hold a moment, release.|10|0ﻼ
Crunches|Lie down, hands join behind neck. Lift your back up to crunch your abs, hold a moment, release.|10|0ﻼ
[Tuesday] Chest|Weights needed|1ﻼ
Dumbbell Pullover| Lie on floor on back, hold dumbbell with both hands above neck. Slowly lower behind your head, don't touch the floor.|10|0ﻼ
Dumbbell Pullover| Lie on floor on back, hold dumbbell with both hands above neck. Slowly lower behind your head, don't touch the floor.|10|0ﻼ
Dumbbell Pullover| Lie on floor on back, hold dumbbell with both hands above neck. Slowly lower behind your head, don't touch the floor.|10|0ﻼ
Weight Stand Pushup|Put weights down shoulder width apart, use them as stands for pushups. Can use your pullup bars if you can't, or hold weight in hands stand, and punch forward with each arm for the time.|10|1ﻼ
Weight Stand Pushup|Put weights down shoulder width apart, use them as stands for pushups. Can use your pullup bars if you can't, or hold weight in hands stand, and punch forward with each arm for the time.|10|1ﻼ
Weight Stand Pushup|Put weights down shoulder width apart, use them as stands for pushups. Can use your pullup bars if you can't, or hold weight in hands stand, and punch forward with each arm for the time.|10|1ﻼ
Weight Stand Pushup|Put weights down shoulder width apart, use them as stands for pushups. Can use your pullup bars if you can't, or hold weight in hands stand, and punch forward with each arm for the time.|10|1ﻼ
Weight Stand Pushup|Put weights down shoulder width apart, use them as stands for pushups. Can use your pullup bars if you can't, or hold weight in hands stand, and punch forward with each arm for the time.|10|1ﻼ
Bridge Dumbbell Press|Shoulders on floor, raise waist till straight from position on back. Do the same as the Flat Dumbell Press without touching the floor.|10|0ﻼ
Bridge Dumbbell Press|Shoulders on floor, raise waist till straight from position on back. Do the same as the Flat Dumbell Press without touching the floor.|10|0ﻼ
Bridge Dumbbell Press|Shoulders on floor, raise waist till straight from position on back. Do the same as the Flat Dumbell Press without touching the floor.|10|0ﻼ
Bridge Dumbbell Press|Shoulders on floor, raise waist till straight from position on back. Do the same as the Flat Dumbell Press without touching the floor.|10|0ﻼ
Flat Dumbbell Press|Lie on back like bench press. Lift weights up in same way. Hold a moment, lower close to 90 degrees, don't touch ground. Hold a moment, then up again.|10|0ﻼ
Flat Dumbbell Press|Lie on back like bench press. Lift weights up in same way. Hold a moment, lower close to 90 degrees, don't touch ground. Hold a moment, then up again.|10|0ﻼ
Flat Dumbbell Press|Lie on back like bench press. Lift weights up in same way. Hold a moment, lower close to 90 degrees, don't touch ground. Hold a moment, then up again.|10|0ﻼ
Flat Dumbbell Press|Lie on back like bench press. Lift weights up in same way. Hold a moment, lower close to 90 degrees, don't touch ground. Hold a moment, then up again.|10|0ﻼ
[Thursday] Back and Lower Abs|Weight needed; Bar needed|1ﻼ
Hanging Leg Raise|Hang from bar in door, raise knees as slow as high as you can, hold a second, lower slowly.|10|0ﻼ
Hanging Leg Raise|Hang from bar in door, raise knees as slow as high as you can, hold a second, lower slowly.|10|0ﻼ
Hanging Leg Raise|Hang from bar in door, raise knees as slow as high as you can, hold a second, lower slowly.|10|0ﻼ
Dumbbell Deadlift|Weight max, put dumbbells on floor in front of you. Wrists face backwards. Pick them up, slow up, slow down, don't touch floor.|10|0ﻼ
Dumbbell Deadlift|Weight max, put dumbbells on floor in front of you. Wrists face backwards. Pick them up, slow up, slow down, don't touch floor.|10|0ﻼ
Dumbbell Deadlift|Weight max, put dumbbells on floor in front of you. Wrists face backwards. Pick them up, slow up, slow down, don't touch floor.|10|0ﻼ
Bent Over Dumbbell Row|Stand by bed, one leg straight on ground, the other knee bent to hold you up on bed, the same side arm hold you up hunched over the side of the bed. Other arm hold weight, slow lift to chest, hold a moment, slow down. Repeat for each arm for the 10 reps each.|10|0ﻼ
Bent Over Dumbbell Row|Stand by bed, one leg straight on ground, the other knee bent to hold you up on bed, the same side arm hold you up hunched over the side of the bed. Other arm hold weight, slow lift to chest, hold a moment, slow down. Repeat for each arm for the 10 reps each.|10|0ﻼ
Bent Over Dumbbell Row|Stand by bed, one leg straight on ground, the other knee bent to hold you up on bed, the same side arm hold you up hunched over the side of the bed. Other arm hold weight, slow lift to chest, hold a moment, slow down. Repeat for each arm for the 10 reps each.|10|0ﻼ
Pullup in Door with Bar|Put bar in door and pull yourself up, don't rush, hold for a second at top.|10|0ﻼ
Pullup in Door with Bar|Put bar in door and pull yourself up, don't rush, hold for a second at top.|10|0ﻼ
Pullup in Door with Bar|Put bar in door and pull yourself up, don't rush, hold for a second at top.|10|0ﻼ
Floor Hyper Extensions|Lie on floor on belly, hands under chin, face down, toes on floor, raise chest hold for 3 seconds. Lower slowly.|10|0ﻼ
Floor Hyper Extensions|Lie on floor on belly, hands under chin, face down, toes on floor, raise chest hold for 3 seconds. Lower slowly.|10|0ﻼ
Floor Hyper Extensions|Lie on floor on belly, hands under chin, face down, toes on floor, raise chest hold for 3 seconds. Lower slowly.|10|0ﻼ
Hanging Leg Raise|Hang from bar in door, raise knees as slow as high as you can, hold a second, lower slowly.|10|0ﻼ
Hanging Leg Raise|Hang from bar in door, raise knees as slow as high as you can, hold a second, lower slowly.|10|0ﻼ
Hanging Leg Raise|Hang from bar in door, raise knees as slow as high as you can, hold a second, lower slowly.|10|0ﻼ
Dumbbell Deadlift|Weight max, put dumbbells on floor in front of you. Wrists face backwards. Pick them up, slow up, slow down, don't touch floor.|10|0ﻼ
Dumbbell Deadlift|Weight max, put dumbbells on floor in front of you. Wrists face backwards. Pick them up, slow up, slow down, don't touch floor.|10|0ﻼ
[Friday] Shoulder and Calves|Weights needed|1ﻼ
Donkey Calf Raises|Stand, bent over forwards, elbows on bed. Do the same as the Standing calf raise again without weights.|10|0ﻼ
Donkey Calf Raises|Stand, bent over forwards, elbows on bed. Do the same as the Standing calf raise again without weights.|10|0ﻼ
Donkey Calf Raises|Stand, bent over forwards, elbows on bed. Do the same as the Standing calf raise again without weights.|10|0ﻼ
Dumbbell Shoulder Press|Sit on chair, wrists face forwards, hold weights to sides of shoulders by head. Lift straight up slow, lower slowly.|10|0ﻼ
Dumbbell Shoulder Press|Sit on chair, wrists face forwards, hold weights to sides of shoulders by head. Lift straight up slow, lower slowly.|10|0ﻼ
Dumbbell Shoulder Press|Sit on chair, wrists face forwards, hold weights to sides of shoulders by head. Lift straight up slow, lower slowly.|10|0ﻼ
Dumbbell Shoulder Press|Sit on chair, wrists face forwards, hold weights to sides of shoulders by head. Lift straight up slow, lower slowly.|10|0ﻼ
Dumbbell Side Lateral Raise|Stand, hold back straight, weight in hands, wrists face your body. Hold chest forwards. Raise to sides till hands at shoulder level, lower, repeat.|10|0ﻼ
Dumbbell Side Lateral Raise|Stand, hold back straight, weight in hands, wrists face your body. Hold chest forwards. Raise to sides till hands at shoulder level, lower, repeat.|10|0ﻼ
Dumbbell Side Lateral Raise|Stand, hold back straight, weight in hands, wrists face your body. Hold chest forwards. Raise to sides till hands at shoulder level, lower, repeat.|10|0ﻼ
Dumbbell Front Lateral Raise|Stand, weights in hands,  raise one arm till hand just above shoulder. Hold a second, then lower. 10 reps each arm.|10|0ﻼ
Dumbbell Front Lateral Raise|Stand, weights in hands,  raise one arm till hand just above shoulder. Hold a second, then lower. 10 reps each arm.|10|0ﻼ
Dumbbell Front Lateral Raise|Stand, weights in hands,  raise one arm till hand just above shoulder. Hold a second, then lower. 10 reps each arm.|10|0ﻼ
Rear Dumbbell Lateral Raise|Sit down, lean so chest on knees, hold weights by ankles, wrists face ankles raise as much as you can to shoulder, hold a second, slow lower.|10|0ﻼ
Rear Dumbbell Lateral Raise|Sit down, lean so chest on knees, hold weights by ankles, wrists face ankles raise as much as you can to shoulder, hold a second, slow lower.|10|0ﻼ
Rear Dumbbell Lateral Raise|Sit down, lean so chest on knees, hold weights by ankles, wrists face ankles raise as much as you can to shoulder, hold a second, slow lower.|10|0ﻼ
Upright Row|Stand, hold weights in hands, facing you. Slow lift to shoulders, then slow down.|10|0ﻼ
Upright Row|Stand, hold weights in hands, facing you. Slow lift to shoulders, then slow down.|10|0ﻼ
Upright Row|Stand, hold weights in hands, facing you. Slow lift to shoulders, then slow down.|10|0ﻼ
Seated Calf Raises|Sit, hold weights on knees, lift your feet to stand on toes, hold at least 3 seconds, then lower.|10|0ﻼ
Seated Calf Raises|Sit, hold weights on knees, lift your feet to stand on toes, hold at least 3 seconds, then lower.|10|0ﻼ
Seated Calf Raises|Sit, hold weights on knees, lift your feet to stand on toes, hold at least 3 seconds, then lower.|10|0ﻼ
Seated Calf Raises|Sit, hold weights on knees, lift your feet to stand on toes, hold at least 3 seconds, then lower.|10|0ﻼ
Donkey Calf Raises|Stand, bent over forwards, elbows on bed. Do the same as the Standing calf raise again without weights.|10|0ﻼ
Donkey Calf Raises|Stand, bent over forwards, elbows on bed. Do the same as the Standing calf raise again without weights.|10|0ﻼ
Standing Calf Raises|MAX. Stand, hold weights, lift yourself on the front of your foot/toes, hold for a few seconds, lower. Last as long as you can.|1|1ﻼ
[Saturday] Arms and Side Abs|Weights required|1ﻼ
Dumbell Crunch|Lie in situp position, hold weights above chest. Do slow situps.|10|0ﻼ
Dumbell Crunch|Lie in situp position, hold weights above chest. Do slow situps.|10|0ﻼ
Dumbell Crunch|Lie in situp position, hold weights above chest. Do slow situps.|10|0ﻼ
Dumbbell Hammer Curl|Stand, hold weights at sides, rists towards one another.  Raise weights to shoulder height to sides fast, lower slowly.|10|0ﻼ
Dumbbell Hammer Curl|Stand, hold weights at sides, rists towards one another.  Raise weights to shoulder height to sides fast, lower slowly.|10|0ﻼ
Dumbbell Hammer Curl|Stand, hold weights at sides, rists towards one another.  Raise weights to shoulder height to sides fast, lower slowly.|10|0ﻼ
Standing Bent Over Two Arm Dumbbell|Stand, hold weights, knees bent, lean forward, arms perpendicular to legs.  Keep head up. Keep torso stationary, lift dumbells to your sides so elbows go behind back, hold a second or two, squeeze back muscles, lower slowly as you inhale.|10|0ﻼ
Standing Bent Over Two Arm Dumbbell|Stand, hold weights, knees bent, lean forward, arms perpendicular to legs.  Keep head up. Keep torso stationary, lift dumbells to your sides so elbows go behind back, hold a second or two, squeeze back muscles, lower slowly as you inhale.|10|0ﻼ
Standing Bent Over Two Arm Dumbbell|Stand, hold weights, knees bent, lean forward, arms perpendicular to legs.  Keep head up. Keep torso stationary, lift dumbells to your sides so elbows go behind back, hold a second or two, squeeze back muscles, lower slowly as you inhale.|10|0ﻼ
Standing Bent Over Two Arm Dumbbell|Stand, hold weights, knees bent, lean forward, arms perpendicular to legs.  Keep head up. Keep torso stationary, lift dumbells to your sides so elbows go behind back, hold a second or two, squeeze back muscles, lower slowly as you inhale.|10|0ﻼ
Preacher curls|Rest your elbows on something, hold them in position, hold weights in hands, wrists facing you. Hold weight and slowly lower and raise them both for the reps.|10|0ﻼ
Preacher curls|Rest your elbows on something, hold them in position, hold weights in hands, wrists facing you. Hold weight and slowly lower and raise them both for the reps.|10|0ﻼ
Preacher curls|Rest your elbows on something, hold them in position, hold weights in hands, wrists facing you. Hold weight and slowly lower and raise them both for the reps.|10|0ﻼ
Preacher curls|Rest your elbows on something, hold them in position, hold weights in hands, wrists facing you. Hold weight and slowly lower and raise them both for the reps.|10|0ﻼ
Skull Crushers|Hold weights behind head while you stand. Slowly raise till arms straight up, slowly lower.|10|0ﻼ
Skull Crushers|Hold weights behind head while you stand. Slowly raise till arms straight up, slowly lower.|10|0ﻼ
Skull Crushers|Hold weights behind head while you stand. Slowly raise till arms straight up, slowly lower.|10|0ﻼ
Skull Crushers|Hold weights behind head while you stand. Slowly raise till arms straight up, slowly lower.|10|0ﻼ
Reverse Grip Dumbbells Curls|Stand, hold weights at waist with wrists facing body. Curl arm up, keep wrist facing ground, curl down, both slow.|10|0ﻼ
Reverse Grip Dumbbells Curls|Stand, hold weights at waist with wrists facing body. Curl arm up, keep wrist facing ground, curl down, both slow.|10|0ﻼ
Reverse Grip Dumbbells Curls|Stand, hold weights at waist with wrists facing body. Curl arm up, keep wrist facing ground, curl down, both slow.|10|0ﻼ
Dumbell Crunch|Lie in situp position, hold weights above chest. Do slow situps.|10|0ﻼ
Dumbell Crunch|Lie in situp position, hold weights above chest. Do slow situps.|10|0ﻼ
Dumbbell Hammer Curl|Stand, hold weights at sides, rists towards one another.  Raise weights to shoulder height to sides fast, lower slowly.|10|0ﻼ
Dumbbell Hammer Curl|Stand, hold weights at sides, rists towards one another.  Raise weights to shoulder height to sides fast, lower slowly.|10|0ﻼ
[Wednesday/Sunday] Rest/Stretch|No weights needed|0ﻼ
Extra Notes|Note down what you can feel.|1|0ﻼ
Side Stretch|Lean over left a bit, hold a little, then right, hold a little.|1|0ﻼ
Shoulder and Neck Stretch|Move your head around 360 degrees, move shoulders around as much as you can with arms stretched out.|1|0ﻼ
Tricep Stretch| Put your arm over your head, use the other arm to press down a little, then do the other side.|1|0ﻼ
Bicep Stretch| One at a time hold your arms across the front of your chest, use the other arm to pull it in a bit, each side.|1|0ﻼ
Upper Back Stretch|Hold hands up to side, use door to press back on arms, hold a bit.|1|0ﻼ
Chest and Shoulder Stretch|Stand, hold hands behind your back, keep arms straight. Lift behind you as high as is comfortable and hold for a short while.|1|0ﻼ
Hip Stretch|Lie on back, bring one knee up to chest as close as you can, hold for a bit with hands, then the other leg.|1|0ﻼ
Hamstring Stretch|Sit on floor, legs straight in front of you, bend forward as far as possible while keeping legs straight, hold for a while.|1|0ﻼ
Thigh Stretch|Stand on one leg at a time, raise the other behind you, stretch it up, a few times each.|1|0ﻼ
[Everyday] Warmups|Stretch and get ready [Seconds]|0ﻼ
Torso Rotations|Stand, put hands on hips, rotate torso around while keeping shoulder still.|1|1ﻼ
Half Jacks|Hands on waist, jump and move feet outwards, jump and move feet inwards, continuous.|1|1ﻼ
Arm Rotations|Stand, arms out to sides, move arms around while straight, reach in as many directions as you can.|1|1ﻼ
Half Jacks|Hands on waist, jump and move feet outwards, jump and move feet inwards, continuous.|1|1ﻼ
Chest Expansions|Stand, arms out to sides, move chest forwards, hold a few seconds, then back.|1|1ﻼ
Half Jacks|Hands on waist, jump and move feet outwards, jump and move feet inwards, continuous.|1|1ﻼ
[Wednesday] Measure|Measurements|0ﻼ
Well Done!|That's another week!|0|0ﻼ
Thoughts|in words|0|0ﻼ
Belly|in cm|0|0ﻼ
Weight|in kg|0|0ﻼ

Final Thoughts

I’m fairly certain the above implementation could use more work; even to minimise the code some more. You’ll note with Release compile, and code minimisation, we drop down to an apk of size around 17.7mb.

You can get the code and take a look at it for yourself. File size: 4.7mb, hasn’t been cleaned.

Note: If you open the code and it shows errors for Mono.Android you need to remove the reference and add it again: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v11.0\Mono.Android.dll
Your MonoAndroid\v11.0 may be an older or newer reference version, I’m unsure if more would be needed.

Please note, you can try out the APK for yourself at your own risk: za.org.loveland.edgehelper.apk – 17.3mb

The note for the size, if I didn’t mention it properly before, is the original EdgeHelper I made was a disastrous 110mb+. For release on the Android store we’d need to keep it smaller. The new implementation when I swapped to use of C# 9.0 for the project here today became tiny in comparison.

I’d love for fellow developers to let me know how it seems to them, where I can improve this first proof of concept, and all ideas it could bring.

I’ll release the APK, as mentioned above, when I’m finished adding more functionality for myself. People can let me know if they’d love it in full form.

I needed to restart the Android emulator and/or VS2019 at times, I’m sure it will be sorted eventually, not that I know why it happened.

When you add Exercises and ExerciseSets don’t use the character ‘|’, I’m a little lazy, thanks.