This commit is contained in:
Tsanie Lily 2022-02-23 16:09:24 +08:00
commit 11f4666b8e
146 changed files with 32476 additions and 0 deletions

405
.gitignore vendored Normal file
View File

@ -0,0 +1,405 @@
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/

61
Billing.Shared/App.cs Normal file
View File

@ -0,0 +1,61 @@
using Billing.Languages;
using Billing.Themes;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Billing;
public class App : Application
{
public static AppTheme CurrentTheme { get; private set; }
public static PlatformCulture CurrentCulture { get; private set; }
public App()
{
CurrentCulture = new PlatformCulture();
InitResources();
MainPage = new MainShell();
Shell.Current.GoToAsync("//Bills");
}
protected override void OnStart()
{
}
protected override void OnResume()
{
SetTheme(AppInfo.RequestedTheme);
}
private void InitResources()
{
var theme = AppInfo.RequestedTheme;
SetTheme(theme, true);
}
private void SetTheme(AppTheme theme, bool force = false)
{
if (force || theme != CurrentTheme)
{
CurrentTheme = theme;
}
else
{
return;
}
Helper.Debug($"application theme: {theme}");
BaseTheme instance;
if (theme == AppTheme.Dark)
{
instance = Dark.Instance;
}
else
{
instance = Light.Instance;
}
// TODO: status bar
Resources = instance;
}
}

View File

@ -0,0 +1,3 @@
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>6ac75d01-70d6-4a07-8685-bc52afd97a7a</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Billing</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Languages\en.xml" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Languages\zh-CN.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)App.cs" />
<Compile Include="$(MSBuildThisFileDirectory)AssemblyInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Languages\PlatformCulture.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Languages\Resource.cs" />
<Compile Include="$(MSBuildThisFileDirectory)MainShell.xaml.cs">
<DependentUpon>MainShell.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Themes\BaseTheme.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Themes\Dark.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Themes\Light.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UI\BillingDate.xaml.cs">
<DependentUpon>BillingDate.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)UI\BillingPage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UI\Converters.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UI\CustomControl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)UI\Definition.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Views\AccountPage.xaml.cs">
<DependentUpon>AccountPage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Views\AddBillPage.xaml.cs">
<DependentUpon>AddBillPage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Views\BillPage.xaml.cs">
<DependentUpon>BillPage.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Views\SettingPage.xaml.cs">
<DependentUpon>SettingPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)MainShell.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)UI\BillingDate.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Views\AccountPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Views\AddBillPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Views\BillPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Views\SettingPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>6ac75d01-70d6-4a07-8685-bc52afd97a7a</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<Import Project="Billing.Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

23
Billing.Shared/Helper.cs Normal file
View File

@ -0,0 +1,23 @@
using System;
namespace Billing;
internal static class Helper
{
public static void Debug(string message)
{
var time = DateTime.Now.ToString("HH:mm:ss.fff");
System.Diagnostics.Debug.WriteLine($"[{time}] - {message}");
}
public static void Error(string category, Exception ex)
{
Error(category, ex?.Message ?? "unknown error");
}
public static void Error(string category, string message)
{
var time = DateTime.Now.ToString("HH:mm:ss.fff");
System.Diagnostics.Debug.Fail($"[{time}] - {category}", message);
}
}

View File

@ -0,0 +1,61 @@
namespace Billing.Languages
{
public partial class PlatformCulture
{
public string PlatformString { get; set; }
public string LanguageCode { get; set; }
public string LocaleCode { get; set; }
public string Language => string.IsNullOrEmpty(LocaleCode) ? LanguageCode : LanguageCode + "-" + LocaleCode;
public partial string GetNamespace();
public partial void Init();
public PlatformCulture()
{
Init();
}
private void Init(string cultureString)
{
if (string.IsNullOrEmpty(cultureString))
{
cultureString = "zh-CN";
}
PlatformString = cultureString.Replace('_', '-');
if (PlatformString.Contains('-'))
{
var parts = PlatformString.Split('-');
LanguageCode = parts[0];
LocaleCode = parts[^1];
}
else
{
LanguageCode = PlatformString;
LocaleCode = string.Empty;
}
}
private string ToDotnetFallbackLanguage()
{
string netLanguage = LanguageCode switch
{
// fallback to Portuguese (Portugal)
"pt" => "pt-PT",
// equivalent to German (Switzerland) for this app
"gsw" => "de-CH",
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
// use the first part of the identifier (two chars, usually);
_ => LanguageCode,
};
Helper.Debug($".NET Fallback Language/Locale: {LanguageCode} to {netLanguage} (application-specific)");
return netLanguage;
}
public override string ToString() => PlatformString;
}
}

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Billing.Languages
{
internal class Resource
{
public static string TitleDateFormat => Text(nameof(TitleDateFormat));
static readonly Dictionary<string, LanguageResource> dict = new();
public static string Text(string name, params object[] args)
{
string current = App.CurrentCulture.PlatformString;
if (!dict.TryGetValue(current, out LanguageResource lang))
{
lang = new LanguageResource(App.CurrentCulture);
dict.Add(current, lang);
}
if (args == null || args.Length == 0)
{
return lang[name];
}
return string.Format(lang[name], args);
}
private class LanguageResource
{
private readonly Dictionary<string, string> strings;
public string this[string key]
{
get
{
if (strings?.TryGetValue(key, out string val) == true)
{
return val;
}
return key;
}
}
public LanguageResource(PlatformCulture lang)
{
try
{
var ns = lang.GetNamespace();
var langId = $"{ns}.Languages.{lang.Language}.xml";
var langCodeId = $"{ns}.Languages.{lang.LanguageCode}.xml";
var assembly = typeof(LanguageResource).Assembly;
var name = assembly.GetManifestResourceNames().FirstOrDefault(n =>
string.Equals(n, langId, StringComparison.OrdinalIgnoreCase) ||
string.Equals(n, langCodeId, StringComparison.OrdinalIgnoreCase));
if (name == null)
{
name = $"{ns}.Languages.zh-CN.xml";
}
var xml = new XmlDocument();
using (var stream = assembly.GetManifestResourceStream(name))
{
xml.Load(stream);
}
strings = new Dictionary<string, string>();
foreach (XmlElement ele in xml.DocumentElement)
{
strings[ele.Name] = ele.InnerText;
}
}
catch (Exception ex)
{
Helper.Error("language.ctor", $"failed to load json resource: {lang}, {ex.Message}");
}
}
}
}
[ContentProperty(nameof(Text))]
public class TextExtension : IMarkupExtension
{
public string Text { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Text == null)
{
return string.Empty;
}
return Resource.Text(Text);
}
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<Accounts>Accounts</Accounts>
<Bills>Bills</Bills>
<Settings>Settings</Settings>
<Sunday>Su</Sunday>
<Monday>Mo</Monday>
<Tuesday>Tu</Tuesday>
<Wednesday>We</Wednesday>
<Thursday>Th</Thursday>
<Friday>Fr</Friday>
<Saturday>Sa</Saturday>
<NoRecords>Bills not yet generated</NoRecords>
<Memo>Click here to record</Memo>
<TitleDateFormat>MM/dd/yyyy</TitleDateFormat>
</root>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<Accounts>账户</Accounts>
<Bills>账单</Bills>
<Settings>设置</Settings>
<Sunday>周日</Sunday>
<Monday>周一</Monday>
<Tuesday>周二</Tuesday>
<Wednesday>周三</Wednesday>
<Thursday>周四</Thursday>
<Friday>周五</Friday>
<Saturday>周六</Saturday>
<NoRecords>还未产生账单</NoRecords>
<Memo>点此记录</Memo>
<TitleDateFormat>yyyy年MM月dd日</TitleDateFormat>
</root>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:v="clr-namespace:Billing.Views"
xmlns:r="clr-namespace:Billing.Languages"
x:Class="Billing.MainShell"
BackgroundColor="{DynamicResource WindowBackgroundColor}"
ForegroundColor="{DynamicResource PrimaryColor}"
TitleColor="{DynamicResource PrimaryColor}"
Shell.NavBarHasShadow="True">
<TabBar>
<ShellContent ContentTemplate="{DataTemplate v:AccountPage}" Route="Accounts" Title="{r:Text Accounts}" Icon="wallet.png"/>
<ShellContent ContentTemplate="{DataTemplate v:BillPage}" Route="Bills" Title="{r:Text Bills}" Icon="bill.png"/>
<ShellContent ContentTemplate="{DataTemplate v:SettingPage}" Route="Settings" Title="{r:Text Settings}" Icon="settings.png"/>
</TabBar>
</Shell>

View File

@ -0,0 +1,11 @@
using Xamarin.Forms;
namespace Billing;
public partial class MainShell : Shell
{
public MainShell()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,65 @@
using Billing.UI;
using Xamarin.Forms;
namespace Billing.Themes;
public abstract class BaseTheme : ResourceDictionary
{
public const string CascadiaFontRegular = nameof(CascadiaFontRegular);
public const string CascadiaFontBold = nameof(CascadiaFontBold);
public const string RobotoCondensedFontRegular = nameof(RobotoCondensedFontRegular);
public const string RobotoCondensedFontBold = nameof(RobotoCondensedFontBold);
public const string WindowBackgroundColor = nameof(WindowBackgroundColor);
public const string PromptBackgroundColor = nameof(PromptBackgroundColor);
public const string PrimaryColor = nameof(PrimaryColor);
public const string SecondaryColor = nameof(SecondaryColor);
public const string TabBarBackgroundColor = nameof(TabBarBackgroundColor);
public const string TabBarTitleColor = nameof(TabBarTitleColor);
public const string TabBarUnselectedColor = nameof(TabBarUnselectedColor);
public const string OutRangeDayColor = nameof(OutRangeDayColor);
public const string TextColor = nameof(TextColor);
public const string WeekendColor = nameof(WeekendColor);
protected abstract Color PrimaryMauiColor { get; }
protected abstract Color SecondaryMauiColor { get; }
protected void InitResources()
{
var robotoRegularFontFamily = Definition.GetRobotoCondensedRegularFontFamily();
Add(CascadiaFontRegular, Definition.GetCascadiaRegularFontFamily());
Add(CascadiaFontBold, Definition.GetCascadiaBoldFontFamily());
Add(RobotoCondensedFontRegular, Definition.GetRobotoCondensedRegularFontFamily());
Add(RobotoCondensedFontBold, Definition.GetRobotoCondensedBoldFontFamily());
Add(PrimaryColor, PrimaryMauiColor);
Add(SecondaryColor, SecondaryMauiColor);
Add(TabBarTitleColor, PrimaryMauiColor);
Add(new Style(typeof(Label))
{
Setters =
{
new Setter { Property = Label.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Label)) },
new Setter { Property = Label.TextColorProperty, Value = PrimaryMauiColor },
new Setter { Property = Label.FontFamilyProperty, Value = robotoRegularFontFamily }
}
});
Add(new Style(typeof(Button))
{
Setters =
{
new Setter { Property = Button.TextColorProperty, Value = SecondaryMauiColor },
new Setter { Property = Button.FontFamilyProperty, Value = robotoRegularFontFamily },
new Setter { Property = VisualElement.BackgroundColorProperty, Value = PrimaryMauiColor },
new Setter { Property = Button.PaddingProperty, Value = new Thickness(14, 10) }
}
});
Add(new Style(typeof(TintImage))
{
Setters =
{
new Setter { Property = TintImage.PrimaryColorProperty, Value = PrimaryMauiColor }
}
});
}
}

View File

@ -0,0 +1,40 @@
using Xamarin.Forms;
namespace Billing.Themes;
public class Dark : BaseTheme
{
private static Dark _instance;
public static Dark Instance => _instance ??= new Dark();
protected override Color PrimaryMauiColor => Color.White;
protected override Color SecondaryMauiColor => Color.LightGray;
public Dark()
{
InitColors();
InitResources();
}
private void InitColors()
{
Add(WindowBackgroundColor, Color.Black);
Add(PromptBackgroundColor, Color.FromRgb(0x1f, 0x1f, 0x1f));
Add(TabBarBackgroundColor, Color.Black);
Add(TabBarUnselectedColor, Color.FromRgb(0x82, 0x82, 0x82));
Add(OutRangeDayColor, Color.DarkGray);
Add(TextColor, Color.FromRgb(0xcc, 0xcc, 0xcc));
Add(WeekendColor, Color.FromRgb(211, 5, 5));
Add(new Style(typeof(TabBar))
{
Setters =
{
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.Black },
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor },
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
}
});
}
}

View File

@ -0,0 +1,40 @@
using Xamarin.Forms;
namespace Billing.Themes;
public class Light : BaseTheme
{
private static Light _instance;
public static Light Instance => _instance ??= new Light();
protected override Color PrimaryMauiColor => Color.FromRgb(0x18, 0x31, 0x53);
protected override Color SecondaryMauiColor => Color.White;
public Light()
{
InitColors();
InitResources();
}
private void InitColors()
{
Add(WindowBackgroundColor, Color.White);
Add(PromptBackgroundColor, Color.FromRgb(0xe0, 0xe0, 0xe0));
Add(TabBarBackgroundColor, Color.White);
Add(TabBarUnselectedColor, Color.FromRgb(0x82, 0x82, 0x82));
Add(OutRangeDayColor, Color.LightGray);
Add(TextColor, Color.FromRgb(0x33, 0x33, 0x33));
Add(WeekendColor, Color.FromRgb(211, 64, 85));
Add(new Style(typeof(TabBar))
{
Setters =
{
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.White },
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor },
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
}
});
}
}

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.UI.BillingDate"
x:DataType="ui:BillingDate"
x:Name="billingDate"
BindingContext="{x:Reference billingDate}">
<ContentView.Resources>
<Style x:Key="centerLabel" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center"/>
<Setter Property="FontSize" Value="Small"/>
<Setter Property="TextColor" Value="{DynamicResource TextColor}"/>
</Style>
<Style x:Key="dateLabel" TargetType="Label">
<Setter Property="HorizontalOptions" Value="Center"/>
</Style>
<ControlTemplate x:Key="weekDay">
<Grid Padding="0, 8, 0, 0" RowDefinitions="Auto, 3" RowSpacing="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnDayTapped, Source={x:Reference billingDate}}" CommandParameter="{TemplateBinding BillingDay}"/>
</Grid.GestureRecognizers>
<Label Style="{StaticResource dateLabel}" Text="{TemplateBinding BillingDay.Text}"
TextColor="{DynamicResource TextColor}"
FontFamily="{TemplateBinding BillingDay.FontFamily}"
Opacity="{TemplateBinding BillingDay.TextOpacity}"/>
<StackLayout Grid.Row="1"
IsVisible="{TemplateBinding BillingDay.IsSelected}"
BackgroundColor="{DynamicResource PrimaryColor}"
Opacity="{TemplateBinding BillingDay.Opacity}"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="weekEnd">
<Grid Padding="0, 8, 0, 0" RowDefinitions="Auto, 3" RowSpacing="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnDayTapped, Source={x:Reference billingDate}}" CommandParameter="{TemplateBinding BillingDay}"/>
</Grid.GestureRecognizers>
<Label Style="{StaticResource dateLabel}" Text="{TemplateBinding BillingDay.Text}"
TextColor="{DynamicResource WeekendColor}"
FontFamily="{TemplateBinding BillingDay.FontFamily}"
Opacity="{TemplateBinding BillingDay.TextOpacity}"/>
<StackLayout Grid.Row="1"
IsVisible="{TemplateBinding BillingDay.IsSelected}"
BackgroundColor="{DynamicResource PrimaryColor}"
Opacity="{TemplateBinding BillingDay.Opacity}"/>
</Grid>
</ControlTemplate>
<Style TargetType="ui:BillingDayView">
<Setter Property="ControlTemplate" Value="{StaticResource weekDay}"/>
</Style>
</ContentView.Resources>
<ContentView.Content>
<Grid Padding="0, 8, 0, 4" ColumnDefinitions="*,*,*,*,*,*,*" RowDefinitions="Auto, Auto">
<Grid.GestureRecognizers>
<PanGestureRecognizer PanUpdated="OnPanUpdated"/>
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="{r:Text Sunday}" Style="{StaticResource centerLabel}"/>
<Label Grid.Column="1" Text="{r:Text Monday}" Style="{StaticResource centerLabel}"/>
<Label Grid.Column="2" Text="{r:Text Tuesday}" Style="{StaticResource centerLabel}"/>
<Label Grid.Column="3" Text="{r:Text Wednesday}" Style="{StaticResource centerLabel}"/>
<Label Grid.Column="4" Text="{r:Text Thursday}" Style="{StaticResource centerLabel}"/>
<Label Grid.Column="5" Text="{r:Text Friday}" Style="{StaticResource centerLabel}"/>
<Label Grid.Column="6" Text="{r:Text Saturday}" Style="{StaticResource centerLabel}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="0" BillingDay="{Binding Sunday}" ControlTemplate="{StaticResource weekEnd}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="1" BillingDay="{Binding Monday}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="2" BillingDay="{Binding Tuesday}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="3" BillingDay="{Binding Wednesday}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="4" BillingDay="{Binding Thursday}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="5" BillingDay="{Binding Friday}"/>
<ui:BillingDayView Grid.Row="1" Grid.Column="6" BillingDay="{Binding Saturday}" ControlTemplate="{StaticResource weekEnd}"/>
</Grid>
</ContentView.Content>
</ContentView>

View File

@ -0,0 +1,290 @@
using Billing.Themes;
using System;
using Xamarin.Forms;
namespace Billing.UI;
public partial class BillingDate : ContentView
{
#region UI Properties
private static readonly BindableProperty SundayProperty = BindableProperty.Create(nameof(Sunday), typeof(BillingDay), typeof(BillingDate));
private static readonly BindableProperty MondayProperty = BindableProperty.Create(nameof(Monday), typeof(BillingDay), typeof(BillingDate));
private static readonly BindableProperty TuesdayProperty = BindableProperty.Create(nameof(Tuesday), typeof(BillingDay), typeof(BillingDate));
private static readonly BindableProperty WednesdayProperty = BindableProperty.Create(nameof(Wednesday), typeof(BillingDay), typeof(BillingDate));
private static readonly BindableProperty ThursdayProperty = BindableProperty.Create(nameof(Thursday), typeof(BillingDay), typeof(BillingDate));
private static readonly BindableProperty FridayProperty = BindableProperty.Create(nameof(Friday), typeof(BillingDay), typeof(BillingDate));
private static readonly BindableProperty SaturdayProperty = BindableProperty.Create(nameof(Saturday), typeof(BillingDay), typeof(BillingDate));
public BillingDay Sunday => (BillingDay)GetValue(SundayProperty);
public BillingDay Monday => (BillingDay)GetValue(MondayProperty);
public BillingDay Tuesday => (BillingDay)GetValue(TuesdayProperty);
public BillingDay Wednesday => (BillingDay)GetValue(WednesdayProperty);
public BillingDay Thursday => (BillingDay)GetValue(ThursdayProperty);
public BillingDay Friday => (BillingDay)GetValue(FridayProperty);
public BillingDay Saturday => (BillingDay)GetValue(SaturdayProperty);
#endregion
private static BindableProperty GetWeekProperty(int week)
{
return (DayOfWeek)week switch
{
DayOfWeek.Monday => MondayProperty,
DayOfWeek.Tuesday => TuesdayProperty,
DayOfWeek.Wednesday => WednesdayProperty,
DayOfWeek.Thursday => ThursdayProperty,
DayOfWeek.Friday => FridayProperty,
DayOfWeek.Saturday => SaturdayProperty,
_ => SundayProperty
};
}
public static readonly BindableProperty LocatedDateProperty = BindableProperty.Create(nameof(LocatedDate), typeof(DateTime), typeof(BillingDate), propertyChanged: OnLocatedDatePropertyChanged);
public static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillingDate), propertyChanged: OnSelectedDatePropertyChanged);
private static void OnLocatedDatePropertyChanged(BindableObject obj, object old, object @new)
{
if (obj is BillingDate billingDate && @new is DateTime date)
{
var week = (int)date.DayOfWeek;
var tmpDate = date.AddDays(-week);
for (var i = 0; i < 7; i++)
{
var prop = GetWeekProperty(i);
var day = new BillingDay(tmpDate);
billingDate.SetValue(prop, day);
tmpDate = tmpDate.AddDays(1);
}
}
}
private static void OnSelectedDatePropertyChanged(BindableObject obj, object old, object @new)
{
if (obj is BillingDate billingDate && @new is DateTime selected)
{
for (var i = 0; i < 7; i++)
{
var prop = GetWeekProperty(i);
var day = (BillingDay)billingDate.GetValue(prop);
day.Refresh(selected);
}
}
}
public DateTime LocatedDate
{
get => (DateTime)GetValue(LocatedDateProperty);
set => SetValue(LocatedDateProperty, value);
}
public DateTime SelectedDate
{
get => (DateTime)GetValue(SelectedDateProperty);
set => SetValue(SelectedDateProperty, value);
}
public Command OnDayTapped { get; }
public event EventHandler<DateEventArgs> DateSelected;
private DateTime lastDate;
private double? x1;
private double x2;
public BillingDate()
{
OnDayTapped = new Command(OnDayChanged);
InitializeComponent();
}
public void SetDateTime(DateTime selectedDate, DateTime? locatedDate = null)
{
if (locatedDate != null)
{
LocatedDate = locatedDate.Value;
}
else
{
LocatedDate = selectedDate;
}
SelectedDate = selectedDate;
DateSelected?.Invoke(this, new DateEventArgs(selectedDate));
}
private void OnDayChanged(object o)
{
var selected = SelectedDate;
if (o is BillingDay day && (selected.Year != day.Date.Year || selected.DayOfYear != day.Date.DayOfYear))
{
for (var i = 0; i < 7; i++)
{
var d = (BillingDay)GetValue(GetWeekProperty(i));
if (d.IsSelected)
{
this.AbortAnimation("unselected");
this.Animate("unselected", v =>
{
d.Opacity = v;
},
start: 1, end: 0,
easing: Easing.CubicOut,
finished: (v, b) =>
{
d.Opacity = 0;
d.IsSelected = false;
});
}
}
SelectedDate = day.Date;
this.AbortAnimation("selected");
this.Animate("selected", v =>
{
day.Opacity = v;
},
start: 0, end: 1,
easing: Easing.CubicOut,
finished: (v, b) =>
{
day.Opacity = 1;
});
DateSelected?.Invoke(this, new DateEventArgs(day.Date));
}
}
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
if (e.StatusType == GestureStatus.Started)
{
lastDate = DateTime.Now;
x1 = null;
}
else if (e.StatusType == GestureStatus.Running)
{
if (x1 == null)
{
x1 = e.TotalX;
}
x2 = e.TotalX;
}
else if (e.StatusType == GestureStatus.Completed)
{
if (x1 == null)
{
return;
}
var totalX = x2 - x1.Value;
x1 = null;
var ms = (DateTime.Now - lastDate).TotalMilliseconds;
var speed = totalX / ms;
Helper.Debug($"completed, speed: {speed}");
if (speed < -0.7)
{
LocatedDate = LocatedDate.AddDays(7);
}
else if (speed > 0.7)
{
LocatedDate = LocatedDate.AddDays(-7);
}
OnSelectedDatePropertyChanged(this, null, SelectedDate);
}
}
}
public class DateEventArgs : EventArgs
{
public DateTime Date { get; }
public DateEventArgs(DateTime date)
{
Date = date;
}
}
public class BillingDayView : ContentView
{
public static readonly BindableProperty BillingDayProperty = BindableProperty.Create(nameof(BillingDay), typeof(BillingDay), typeof(BillingDayView));
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(Command), typeof(BillingDayView));
public BillingDay BillingDay
{
get => (BillingDay)GetValue(BillingDayProperty);
set => SetValue(BillingDayProperty, value);
}
public Command Command
{
get => (Command)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
}
public class BillingDay : BindableObject
{
private static readonly BindableProperty DateProperty = BindableProperty.Create(nameof(Date), typeof(DateTime), typeof(BillingDay), propertyChanged: OnDatePropertyChanged);
private static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(BillingDay));
private static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(BillingDay), defaultValue: Definition.GetCascadiaRegularFontFamily());
private static readonly BindableProperty IsSelectedProperty = BindableProperty.Create(nameof(IsSelected), typeof(bool), typeof(BillingDay));
private static readonly BindableProperty OpacityProperty = BindableProperty.Create(nameof(Opacity), typeof(double), typeof(BillingDay), defaultValue: 1.0);
private static readonly BindableProperty TextOpacityProperty = BindableProperty.Create(nameof(TextOpacity), typeof(double), typeof(BillingDay), defaultValue: 1.0);
private static void OnDatePropertyChanged(BindableObject obj, object old, object @new)
{
if (obj is BillingDay day && @new is DateTime date)
{
if (date.Day == 1)
{
day.SetValue(TextProperty, date.ToString("MMM"));
}
else
{
day.SetValue(TextProperty, date.Day.ToString());
}
}
}
public DateTime Date
{
get => (DateTime)GetValue(DateProperty);
set => SetValue(DateProperty, value);
}
public string Text => (string)GetValue(TextProperty);
public string FontFamily => (string)GetValue(FontFamilyProperty);
public bool IsSelected
{
get => (bool)GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public double Opacity
{
get => (double)GetValue(OpacityProperty);
set => SetValue(OpacityProperty, value);
}
public double TextOpacity => (double)GetValue(TextOpacityProperty);
public BillingDay(DateTime date)
{
Date = date;
}
public void Refresh(DateTime selected)
{
var date = Date;
if (date.Year == selected.Year && date.DayOfYear == selected.DayOfYear)
{
SetValue(IsSelectedProperty, true);
SetValue(FontFamilyProperty, Definition.GetCascadiaBoldFontFamily());
}
else
{
SetValue(FontFamilyProperty, Definition.GetCascadiaRegularFontFamily());
}
if (date.Year == selected.Year && date.Month == selected.Month)
{
SetValue(TextOpacityProperty, 1.0);
}
else
{
SetValue(TextOpacityProperty, .4);
}
}
}

View File

@ -0,0 +1,12 @@
using Billing.Themes;
using Xamarin.Forms;
namespace Billing.UI;
public abstract class BillingPage : ContentPage
{
public BillingPage()
{
SetDynamicResource(BackgroundColorProperty, BaseTheme.WindowBackgroundColor);
}
}

View File

@ -0,0 +1,23 @@
using Billing.Languages;
using System;
using System.Globalization;
using Xamarin.Forms;
namespace Billing.UI;
public class TitleDateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is DateTime date)
{
return date.ToString(Resource.TitleDateFormat);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}

View File

@ -0,0 +1,31 @@
using System;
using Xamarin.Forms;
namespace Billing.UI;
public class TintImage : Image
{
public static readonly BindableProperty PrimaryColorProperty = BindableProperty.Create(nameof(PrimaryColor), typeof(Color?), typeof(TintImage));
public Color? PrimaryColor
{
get => (Color?)GetValue(PrimaryColorProperty);
set => SetValue(PrimaryColorProperty, value);
}
}
public class LongPressButton : Button
{
public event EventHandler LongPressed;
public LongPressButton()
{
Padding = 0;
BackgroundColor = Color.Transparent;
}
public void TriggerLongPress()
{
LongPressed?.Invoke(this, EventArgs.Empty);
}
}

View File

@ -0,0 +1,9 @@
namespace Billing.UI;
public static partial class Definition
{
public static partial string GetCascadiaRegularFontFamily();
public static partial string GetCascadiaBoldFontFamily();
public static partial string GetRobotoCondensedRegularFontFamily();
public static partial string GetRobotoCondensedBoldFontFamily();
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.AccountPage"
Title="{r:Text Accounts}">
<StackLayout>
<Label Text="Welcome to Account Page!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ui:BillingPage>

View File

@ -0,0 +1,11 @@
using Billing.UI;
namespace Billing.Views;
public partial class AccountPage : BillingPage
{
public AccountPage()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.AddBillPage"
x:DataType="v:AddBillPage"
Title="Add Billing">
<StackLayout>
<Label Text="Add a billing here..."
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ui:BillingPage>

View File

@ -0,0 +1,11 @@
using Billing.UI;
namespace Billing.Views;
public partial class AddBillPage : BillingPage
{
public AddBillPage()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:v="clr-namespace:Billing.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.BillPage"
x:DataType="v:BillPage"
x:Name="billPage"
BindingContext="{x:Reference billPage}"
Title="{r:Text Bills}">
<ContentPage.Resources>
<ui:TitleDateConverter x:Key="titleDateConverter"/>
</ContentPage.Resources>
<Shell.TitleView>
<Grid ColumnSpacing="16" ColumnDefinitions="20,*,20">
<ui:TintImage Source="calendar.png" WidthRequest="20" HeightRequest="20" VerticalOptions="Center"/>
<ui:LongPressButton Grid.Column="1" Text="{Binding SelectedDate, Converter={StaticResource titleDateConverter}}"
TextColor="{DynamicResource PrimaryColor}"
HorizontalOptions="{OnPlatform iOS=Center, Android=Start}"
FontFamily="{DynamicResource RobotoCondensedFontBold}"
FontAttributes="Bold" FontSize="20" VerticalOptions="Center"
LongPressed="OnTitleDateLongPressed"/>
</Grid>
</Shell.TitleView>
<Grid RowDefinitions="Auto,*">
<ui:BillingDate x:Name="billingDate" SelectedDate="{Binding SelectedDate}" DateSelected="OnDateSelected"/>
<ScrollView Grid.Row="1">
<Grid Padding="8" ColumnSpacing="8" ColumnDefinitions="Auto, *, Auto"
BackgroundColor="{DynamicResource PromptBackgroundColor}"
VerticalOptions="Start">
<ui:TintImage Source="bars.png" WidthRequest="23" HeightRequest="23"/>
<Label Grid.Column="1" Text="{r:Text NoRecords}" TextColor="{DynamicResource TextColor}"
VerticalOptions="Center"/>
<StackLayout Grid.Column="2" Orientation="Horizontal" Spacing="6">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding AddBilling}"/>
</StackLayout.GestureRecognizers>
<Label Text="{r:Text Memo}" TextColor="{DynamicResource PrimaryColor}"
VerticalOptions="Center"/>
<!--<Label Style="{DynamicResource IconLightStyle}"
Text="{x:Static local:Definition.IconRight}"
TextColor="{DynamicResource TabBarUnselectedColor}"/>-->
<ui:TintImage Source="right.png" WidthRequest="24" HeightRequest="24"/>
</StackLayout>
</Grid>
</ScrollView>
<!--<ui:CircleButton Grid.Row="1" VerticalOptions="End" HorizontalOptions="End"
Margin="20" Padding="0"
BackgroundColor="{DynamicResource PrimaryColor}"
ImageSource="plus.png" HeightRequest="24" WidthRequest="24"/>-->
</Grid>
</ui:BillingPage>

View File

@ -0,0 +1,44 @@
using Billing.UI;
using System;
using Xamarin.Forms;
namespace Billing.Views;
public partial class BillPage : BillingPage
{
private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage));
public DateTime SelectedDate
{
get => (DateTime)GetValue(SelectedDateProperty);
set => SetValue(SelectedDateProperty, value);
}
public Command AddBilling { get; }
public BillPage()
{
AddBilling = new Command(OnAddBilling);
InitializeComponent();
billingDate.SetDateTime(DateTime.Now);
}
private void OnDateSelected(object sender, DateEventArgs e)
{
SelectedDate = e.Date;
// TODO: while selecting date
}
private void OnTitleDateLongPressed(object sender, EventArgs e)
{
billingDate.SetDateTime(DateTime.Now);
}
private async void OnAddBilling()
{
await Navigation.PushAsync(new AddBillPage());
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.SettingPage"
Title="{r:Text Settings}">
<StackLayout>
<Label Text="Welcome to Settings Page!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ui:BillingPage>

View File

@ -0,0 +1,11 @@
using Billing.UI;
namespace Billing.Views;
public partial class SettingPage : BillingPage
{
public SettingPage()
{
InitializeComponent();
}
}

70
Billing.sln Normal file
View File

@ -0,0 +1,70 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32210.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.Android", "Billing\Billing.Android\Billing.Android.csproj", "{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Billing.iOS", "Billing\Billing.iOS\Billing.iOS.csproj", "{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Billing.Shared", "Billing.Shared\Billing.Shared.shproj", "{6AC75D01-70D6-4A07-8685-BC52AFD97A7A}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Billing.Shared\Billing.Shared.projitems*{5c4f1c35-6f66-4063-9605-a9f37fcabba8}*SharedItemsImports = 4
Billing.Shared\Billing.Shared.projitems*{6ac75d01-70d6-4a07-8685-bc52afd97a7a}*SharedItemsImports = 13
Billing.Shared\Billing.Shared.projitems*{b4cd3b27-c58f-4b6b-b60e-35e515a73e5b}*SharedItemsImports = 4
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Release|Any CPU = Release|Any CPU
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|iPhone.Build.0 = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|iPhone.Deploy.0 = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|Any CPU.Build.0 = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|Any CPU.Deploy.0 = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|iPhone.ActiveCfg = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|iPhone.Build.0 = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|iPhone.Deploy.0 = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|Any CPU.Build.0 = Debug|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|Any CPU.Deploy.0 = Debug|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|iPhone.ActiveCfg = Debug|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|iPhone.Build.0 = Debug|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|iPhone.Deploy.0 = Debug|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Debug|iPhoneSimulator.Deploy.0 = Debug|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|Any CPU.Build.0 = Release|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|Any CPU.Deploy.0 = Release|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|iPhone.ActiveCfg = Release|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|iPhone.Build.0 = Release|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|iPhone.Deploy.0 = Release|iPhone
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
{5C4F1C35-6F66-4063-9605-A9F37FCABBA8}.Release|iPhoneSimulator.Deploy.0 = Release|iPhoneSimulator
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6C946C90-CF6E-4464-920A-CD31FF045FC8}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,19 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

Binary file not shown.

View File

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B4CD3B27-C58F-4B6B-B60E-35E515A73E5B}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{6968b3a4-1835-46a3-ac5c-1ae33b475983}</TemplateGuid>
<OutputType>Library</OutputType>
<RootNamespace>Billing.Droid</RootNamespace>
<AssemblyName>Billing.Android</AssemblyName>
<Deterministic>True</Deterministic>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v12.0</TargetFrameworkVersion>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<AndroidUseAapt2>true</AndroidUseAapt2>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<LangVersion>10.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidSupportedAbis>x86_64</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidSupportedAbis>arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="5.0.0.2196" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="Definition.cs" />
<Compile Include="MainActivity.cs" />
<Compile Include="PlatformCulture.cs" />
<Compile Include="Renderers\LongPressButtonRenderer.cs" />
<Compile Include="Renderers\TintImageRenderer.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\CascadiaCode-Bold.ttf" />
<AndroidAsset Include="Assets\CascadiaCode-Regular.ttf" />
<AndroidAsset Include="Assets\RobotoCondensed-Regular.ttf" />
<AndroidAsset Include="Assets\RobotoCondensed-Bold.ttf" />
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\styles.xml" />
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\icon.xml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\icon_round.xml" />
<AndroidResource Include="Resources\mipmap-hdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-hdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-mdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-mdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\drawable\xamarin_logo.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\wallet.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\calendar.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\bars.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\bill.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\plus.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\right.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\settings.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\wallet.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\bars.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\bill.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\plus.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\right.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\settings.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\wallet.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\bars.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\bill.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\plus.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\right.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\settings.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\bars.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\bill.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\plus.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\right.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\settings.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\wallet.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\calendar.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\calendar.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\calendar.png" />
</ItemGroup>
<Import Project="..\..\Billing.Shared\Billing.Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

View File

@ -0,0 +1,9 @@
namespace Billing.UI;
public static partial class Definition
{
public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular.ttf#CascadiaCode-Regular";
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold.ttf#CascadiaCode-Bold";
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular.ttf#RobotoCondensed-Regular";
public static partial string GetRobotoCondensedBoldFontFamily() => "RobotoCondensed-Bold.ttf#RobotoCondensed-Bold";
}

View File

@ -0,0 +1,31 @@
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
namespace Billing.Droid;
[Activity(
Label = "Billing",
Icon = "@mipmap/icon",
RoundIcon = "@mipmap/icon_round",
Theme = "@style/MainTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}

View File

@ -0,0 +1,66 @@
using Billing.Droid;
using System.Globalization;
using System.Threading;
namespace Billing.Languages;
public partial class PlatformCulture
{
public partial string GetNamespace()
{
return typeof(MainActivity).Namespace;
}
public partial void Init()
{
var locale = Java.Util.Locale.Default;
string lang = AndroidToDotnetLanguage($"{locale.Language}-{locale.Country}");
CultureInfo ci;
Init(lang);
try
{
ci = new CultureInfo(Language);
}
catch (CultureNotFoundException e)
{
try
{
var fallback = ToDotnetFallbackLanguage();
Helper.Debug($"{lang} failed, trying {fallback} ({e.Message})");
ci = new CultureInfo(fallback);
}
catch (CultureNotFoundException e1)
{
Helper.Error("culture.get", $"{lang} couldn't be set, using 'zh-CN' ({e1.Message})");
ci = new CultureInfo("zh-CN");
}
}
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
Helper.Debug($"CurrentCulture set: {ci.Name}");
}
private static string AndroidToDotnetLanguage(string androidLanguage)
{
//certain languages need to be converted to CultureInfo equivalent
string netLanguage = androidLanguage switch
{
// Not supported .NET culture
"ms-BN" or "ms-MY" or "ms-SG" => "ms", // closest supported
// "Indonesian (Indonesia)" has different code in .NET
"in-ID" => "id-ID", // correct code for .NET
// "Schwiizertüütsch (Swiss German)" not supported .NET culture
"gsw-CH" => "de-CH", // closest supported
// add more application-specific cases here (if required)
// ONLY use cultures that have been tested and known to work
_ => androidLanguage,
};
Helper.Debug($"Android Language: {androidLanguage}, .NET Language/Locale: {netLanguage}");
return netLanguage;
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="org.tsanie.billing" android:installLocation="auto">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="31" />
<application android:label="Billing" android:theme="@style/MainTheme"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

View File

@ -0,0 +1,29 @@
using System.Reflection;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Billing.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Billing.Android")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// Add some common permissions, these can be removed if not needed
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]

View File

@ -0,0 +1,33 @@
using Android.Content;
using Billing.Droid.Renderers;
using Billing.UI;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(LongPressButton), typeof(LongPressButtonRenderer))]
namespace Billing.Droid.Renderers;
public class LongPressButtonRenderer : ButtonRenderer
{
public LongPressButtonRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Element is LongPressButton && Control != null)
{
Control.LongClick += Control_LongClick;
}
}
private void Control_LongClick(object sender, LongClickEventArgs e)
{
if (Element is LongPressButton button)
{
button.TriggerLongPress();
}
}
}

View File

@ -0,0 +1,36 @@
using Android.Content;
using Billing.Droid.Renderers;
using Billing.UI;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(TintImage), typeof(TintImageRenderer))]
namespace Billing.Droid.Renderers;
public class TintImageRenderer : ImageRenderer
{
public TintImageRenderer(Context context) : base(context)
{
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(TintImage.PrimaryColor) && Control?.Drawable != null && Element is TintImage image)
{
Control.Drawable.SetTint(image.PrimaryColor?.ToAndroid() ?? 0);
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (Control?.Drawable != null && Element is TintImage image)
{
Control.Drawable.SetTint(image.PrimaryColor?.ToAndroid() ?? 0);
}
}
}

View File

@ -0,0 +1,50 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable-hdpi/
icon.png
drawable-ldpi/
icon.png
drawable-mdpi/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called
"Resource" that contains the tokens for each one of the resources included. For example,
for the above Resources layout, this is what the Resource class would expose:
public class Resource {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 863 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/launcher_background" />
<foreground android:drawable="@mipmap/launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/launcher_background" />
<foreground android:drawable="@mipmap/launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="launcher_background">#FFFFFF</color>
<color name="colorPrimary">#183153</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>

View File

@ -0,0 +1,18 @@
<?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>
</resources>

View File

@ -0,0 +1,26 @@
using Foundation;
using UIKit;
namespace Billing.iOS;
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register(nameof(AppDelegate))]
public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
}

View File

@ -0,0 +1,117 @@
{
"images": [
{
"scale": "2x",
"size": "20x20",
"idiom": "iphone",
"filename": "Icon40.png"
},
{
"scale": "3x",
"size": "20x20",
"idiom": "iphone",
"filename": "Icon60.png"
},
{
"scale": "2x",
"size": "29x29",
"idiom": "iphone",
"filename": "Icon58.png"
},
{
"scale": "3x",
"size": "29x29",
"idiom": "iphone",
"filename": "Icon87.png"
},
{
"scale": "2x",
"size": "40x40",
"idiom": "iphone",
"filename": "Icon80.png"
},
{
"scale": "3x",
"size": "40x40",
"idiom": "iphone",
"filename": "Icon120.png"
},
{
"scale": "2x",
"size": "60x60",
"idiom": "iphone",
"filename": "Icon120.png"
},
{
"scale": "3x",
"size": "60x60",
"idiom": "iphone",
"filename": "Icon180.png"
},
{
"scale": "1x",
"size": "20x20",
"idiom": "ipad",
"filename": "Icon20.png"
},
{
"scale": "2x",
"size": "20x20",
"idiom": "ipad",
"filename": "Icon40.png"
},
{
"scale": "1x",
"size": "29x29",
"idiom": "ipad",
"filename": "Icon29.png"
},
{
"scale": "2x",
"size": "29x29",
"idiom": "ipad",
"filename": "Icon58.png"
},
{
"scale": "1x",
"size": "40x40",
"idiom": "ipad",
"filename": "Icon40.png"
},
{
"scale": "2x",
"size": "40x40",
"idiom": "ipad",
"filename": "Icon80.png"
},
{
"scale": "1x",
"size": "76x76",
"idiom": "ipad",
"filename": "Icon76.png"
},
{
"scale": "2x",
"size": "76x76",
"idiom": "ipad",
"filename": "Icon152.png"
},
{
"scale": "2x",
"size": "83.5x83.5",
"idiom": "ipad",
"filename": "Icon167.png"
},
{
"scale": "1x",
"size": "1024x1024",
"idiom": "ios-marketing",
"filename": "Icon1024.png"
}
],
"properties": {},
"info": {
"version": 1,
"author": "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Some files were not shown because too many files have changed in this diff Show More