first test-flight version

This commit is contained in:
Tsanie Lily 2022-03-03 15:10:36 +08:00
parent 9929eee056
commit 25191127f3
116 changed files with 1124 additions and 173 deletions

View File

@ -27,7 +27,7 @@ namespace Billing
InitResources(); InitResources();
MainPage = new MainShell(); MainPage = new MainShell();
Shell.Current.GoToAsync("//Bills"); Shell.Current.GoToAsync("//Settings");
} }
public static void WriteAccounts() => StoreHelper.WriteAccounts(accounts); public static void WriteAccounts() => StoreHelper.WriteAccounts(accounts);
@ -39,12 +39,11 @@ namespace Billing
Helper.Debug($"personal folder: {StoreHelper.PersonalFolder}"); Helper.Debug($"personal folder: {StoreHelper.PersonalFolder}");
Helper.Debug($"cache folder: {StoreHelper.CacheFolder}"); Helper.Debug($"cache folder: {StoreHelper.CacheFolder}");
Task.Run(() =>
{
accounts = StoreHelper.GetAccounts(); accounts = StoreHelper.GetAccounts();
categories = StoreHelper.GetCategories(); categories = StoreHelper.GetCategories();
bills = StoreHelper.GetBills(); bills = StoreHelper.GetBills();
});
Shell.Current.GoToAsync("//Bills");
} }
protected override void OnResume() protected override void OnResume()

View File

@ -10,6 +10,10 @@ namespace Billing.Languages
internal class Resource internal class Resource
{ {
public static string Ok => Text(nameof(Ok)); public static string Ok => Text(nameof(Ok));
public static string Yes => Text(nameof(Yes));
public static string No => Text(nameof(No));
public static string ConfirmDeleteAccount => Text(nameof(ConfirmDeleteAccount));
public static string ConfirmDeleteBill => Text(nameof(ConfirmDeleteBill));
public static string TitleDateFormat => Text(nameof(TitleDateFormat)); public static string TitleDateFormat => Text(nameof(TitleDateFormat));
public static string Cash => Text(nameof(Cash)); public static string Cash => Text(nameof(Cash));
public static string CreditCard => Text(nameof(CreditCard)); public static string CreditCard => Text(nameof(CreditCard));
@ -23,6 +27,20 @@ namespace Billing.Languages
public static string NeedAccount => Text(nameof(NeedAccount)); public static string NeedAccount => Text(nameof(NeedAccount));
public static string AmountRequired => Text(nameof(AmountRequired)); public static string AmountRequired => Text(nameof(AmountRequired));
#region Categories
public static string Clothing => Text(nameof(Clothing));
public static string Food => Text(nameof(Food));
public static string Drinks => Text(nameof(Drinks));
public static string Daily => Text(nameof(Daily));
public static string Trans => Text(nameof(Trans));
public static string Entertainment => Text(nameof(Entertainment));
public static string Learn => Text(nameof(Learn));
public static string Medical => Text(nameof(Medical));
public static string Salary => Text(nameof(Salary));
public static string Earnings => Text(nameof(Earnings));
public static string Bonus => Text(nameof(Bonus));
#endregion
static readonly Dictionary<string, LanguageResource> dict = new(); static readonly Dictionary<string, LanguageResource> dict = new();
public static string Text(string name, params object[] args) public static string Text(string name, params object[] args)

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<Ok>OK</Ok> <Ok>OK</Ok>
<About>About</About>
<Version>Version</Version>
<Preference>Preference</Preference>
<PrimaryColor>Primary Color</PrimaryColor>
<Accounts>Accounts</Accounts> <Accounts>Accounts</Accounts>
<Bills>Bills</Bills> <Bills>Bills</Bills>
<Settings>Settings</Settings> <Settings>Settings</Settings>
@ -44,4 +48,21 @@
<Store>Store</Store> <Store>Store</Store>
<NeedAccount>Please create an account first.</NeedAccount> <NeedAccount>Please create an account first.</NeedAccount>
<AmountRequired>Please enter the amount.</AmountRequired> <AmountRequired>Please enter the amount.</AmountRequired>
<Income>Income</Income>
<Spending>Spending</Spending>
<Clothing>Clothing</Clothing>
<Food>Food</Food>
<Drinks>Drinks</Drinks>
<Daily>Daily</Daily>
<Trans>Transportation</Trans>
<Entertainment>Entertainment</Entertainment>
<Learn>Learn</Learn>
<Medical>Medical</Medical>
<Salary>Salary</Salary>
<Earnings>Earnings</Earnings>
<Bonus>Bonus</Bonus>
<Yes>Yes</Yes>
<No>No</No>
<ConfirmDeleteAccount>Are you sure you want to delete the account?</ConfirmDeleteAccount>
<ConfirmDeleteBill>Are you sure you want to delete the billing?</ConfirmDeleteBill>
</root> </root>

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<Ok>确定</Ok> <Ok>确定</Ok>
<About>关于</About>
<Version>版本号</Version>
<Preference>偏好</Preference>
<PrimaryColor>主要配色</PrimaryColor>
<Accounts>账户</Accounts> <Accounts>账户</Accounts>
<Bills>账单</Bills> <Bills>账单</Bills>
<Settings>设置</Settings> <Settings>设置</Settings>
@ -44,4 +48,21 @@
<Store>店铺</Store> <Store>店铺</Store>
<NeedAccount>请先创建一个资金账户。</NeedAccount> <NeedAccount>请先创建一个资金账户。</NeedAccount>
<AmountRequired>请输入金额。</AmountRequired> <AmountRequired>请输入金额。</AmountRequired>
<Income>收入</Income>
<Spending>支出</Spending>
<Clothing>衣物</Clothing>
<Food>食品</Food>
<Drinks>饮料</Drinks>
<Daily>日常</Daily>
<Trans>交通</Trans>
<Entertainment>娱乐</Entertainment>
<Learn>学习</Learn>
<Medical>医疗</Medical>
<Salary>工资</Salary>
<Earnings>收益</Earnings>
<Bonus>奖金</Bonus>
<Yes></Yes>
<No></No>
<ConfirmDeleteAccount>是否确认删除该账户?</ConfirmDeleteAccount>
<ConfirmDeleteBill>是否确认删除该账单?</ConfirmDeleteBill>
</root> </root>

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Billing.Models; using Billing.Models;
using Billing.UI; using Billing.UI;
using Xamarin.Essentials; using Xamarin.Essentials;
using Resource = Billing.Languages.Resource;
namespace Billing.Store namespace Billing.Store
{ {
@ -59,11 +60,18 @@ namespace Billing.Store
list = new List<Category> list = new List<Category>
{ {
// TODO: sample categories // TODO: sample categories
new() { Id = 0, Name = "早餐", Icon = "face" }, new() { Id = 1, Name = Resource.Clothing, Icon = "clothes" },
new() { Id = 1, Name = "轻轨", Icon = "" }, new() { Id = 2, Name = Resource.Food, Icon = "food" },
new() { Id = 2, Name = "公交车", Icon = "" }, new() { Id = 3, Name = Resource.Drinks, Icon = "drink" },
new() { Id = 3, Name = "出租车", Icon = "" }, new() { Id = 4, Name = Resource.Daily, Icon = "daily" },
new() { Id = 4, Type = CategoryType.Income, Name = "投资", Icon = "#brand#btc" } new() { Id = 5, Name = Resource.Trans, Icon = "trans" },
new() { Id = 6, Name = Resource.Entertainment, Icon = "face" },
new() { Id = 7, Name = Resource.Learn, Icon = "learn" },
new() { Id = 8, Name = Resource.Medical, Icon = "medical" },
new() { Id = 9, Type = CategoryType.Income, Name = Resource.Salary, Icon = "#brand#buffer" },
new() { Id = 10, Type = CategoryType.Income, Name = Resource.Earnings, Icon = "#brand#btc" },
new() { Id = 20, Type = CategoryType.Income, Name = Resource.Bonus, Icon = "dollar" }
}; };
Task.Run(() => WriteCategoriesInternal(list)); Task.Run(() => WriteCategoriesInternal(list));
} }

View File

@ -62,10 +62,26 @@ namespace Billing.Themes
{ {
Setters = Setters =
{ {
new Setter { Property = Editor.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Entry)) }, new Setter { Property = Editor.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(Editor)) },
new Setter { Property = Editor.FontFamilyProperty, Value = robotoRegularFontFamily } new Setter { Property = Editor.FontFamilyProperty, Value = robotoRegularFontFamily }
} }
}); });
Add(new Style(typeof(OptionDatePicker))
{
Setters =
{
new Setter { Property = DatePicker.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(DatePicker)) },
new Setter { Property = DatePicker.FontFamilyProperty, Value = robotoRegularFontFamily }
}
});
Add(new Style(typeof(OptionTimePicker))
{
Setters =
{
new Setter { Property = TimePicker.FontSizeProperty, Value = Device.GetNamedSize(NamedSize.Small, typeof(TimePicker)) },
new Setter { Property = TimePicker.FontFamilyProperty, Value = robotoRegularFontFamily }
}
});
Add(new Style(typeof(Button)) Add(new Style(typeof(Button))
{ {
Setters = Setters =

View File

@ -1,4 +1,6 @@
using Xamarin.Forms; using Billing.UI;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Billing.Themes namespace Billing.Themes
{ {
@ -8,11 +10,25 @@ namespace Billing.Themes
public static Light Instance => _instance ??= new Light(); public static Light Instance => _instance ??= new Light();
protected override Color PrimaryMauiColor => Color.FromRgb(0x18, 0x31, 0x53); private Color primaryColor;
protected override Color PrimaryMauiColor => primaryColor;
protected override Color SecondaryMauiColor => Color.White; protected override Color SecondaryMauiColor => Color.White;
public Light() public Light()
{ {
var color = Preferences.Get(Definition.PrimaryColorKey, "#183153");
primaryColor = Color.FromHex(color);
InitColors();
InitResources();
}
public void RefreshColor(Color primary)
{
primaryColor = primary;
Clear();
InitColors(); InitColors();
InitResources(); InitResources();
} }
@ -35,7 +51,7 @@ namespace Billing.Themes
Setters = Setters =
{ {
new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.White }, new Setter { Property = Shell.TabBarBackgroundColorProperty, Value = Color.White },
new Setter { Property = Shell.TabBarTitleColorProperty, Value = PrimaryMauiColor }, new Setter { Property = Shell.TabBarTitleColorProperty, Value = primaryColor },
new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) } new Setter { Property = Shell.TabBarUnselectedColorProperty, Value = Color.FromRgb(0x82, 0x82, 0x82) }
} }
}); });

View File

@ -1,7 +1,6 @@
using Billing.Languages; using Billing.Languages;
using Billing.Models; using Billing.Models;
using Billing.Themes; using Billing.Themes;
using Billing.Views;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -26,9 +25,26 @@ namespace Billing.UI
} }
} }
public class NegativeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool b)
{
return !b;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class MoneyConverter : IValueConverter public class MoneyConverter : IValueConverter
{ {
public bool MarkVisible { get; set; } = true; public bool MarkVisible { get; set; }
public bool Absolute { get; set; } public bool Absolute { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
@ -89,14 +105,13 @@ namespace Billing.UI
} }
} }
public class UIBillConverter : IValueConverter public class TimeConverter : IValueConverter
{ {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{ {
if (value is UIBill bill) if (value is DateTime time)
{ {
var time = bill.DateCreation.ToString("HH:mm"); return time.ToString("HH:mm");
return $"{time} ({bill.Wallet})";
} }
return string.Empty; return string.Empty;
} }
@ -152,6 +167,7 @@ namespace Billing.UI
{ "appstore", "\uf370" }, { "appstore", "\uf370" },
{ "apple-pay", "\uf416" }, { "apple-pay", "\uf416" },
{ "btc", "\uf15a" }, { "btc", "\uf15a" },
{ "buffer", "\uf837" },
{ "jcb", "\uf24b" }, { "jcb", "\uf24b" },
{ "master-card", "\uf1f1" }, { "master-card", "\uf1f1" },
{ "visa", "\uf1f0" }, { "visa", "\uf1f0" },

View File

@ -29,4 +29,35 @@ namespace Billing.UI
LongPressed?.Invoke(this, EventArgs.Empty); LongPressed?.Invoke(this, EventArgs.Empty);
} }
} }
public class LongPressGrid : Grid
{
public static readonly BindableProperty LongCommandProperty = BindableProperty.Create(nameof(LongCommand), typeof(Command), typeof(LongPressGrid));
public static readonly BindableProperty LongCommandParameterProperty = BindableProperty.Create(nameof(LongCommandParameter), typeof(object), typeof(LongPressGrid));
public Command LongCommand
{
get => (Command)GetValue(LongCommandProperty);
set => SetValue(LongCommandProperty, value);
}
public object LongCommandParameter
{
get => GetValue(LongCommandParameterProperty);
set => SetValue(LongCommandParameterProperty, value);
}
public void TriggerLongPress()
{
var command = LongCommand;
if (command == null)
{
return;
}
var parameter = LongCommandParameter;
if (command.CanExecute(parameter))
{
command.Execute(parameter);
}
}
}
} }

View File

@ -11,6 +11,8 @@ namespace Billing.UI
{ {
public static partial class Definition public static partial class Definition
{ {
public static string PrimaryColorKey = "PrimaryColor";
public static partial (string main, long build) GetVersion();
public static partial string GetCascadiaRegularFontFamily(); public static partial string GetCascadiaRegularFontFamily();
public static partial string GetCascadiaBoldFontFamily(); public static partial string GetCascadiaBoldFontFamily();
public static partial string GetRobotoCondensedRegularFontFamily(); public static partial string GetRobotoCondensedRegularFontFamily();
@ -84,6 +86,13 @@ namespace Billing.UI
{ {
await page.DisplayAlert(title ?? page.Title, message, Resource.Ok); await page.DisplayAlert(title ?? page.Title, message, Resource.Ok);
} }
public static async Task<bool> ShowConfirm(this Page page, string message)
{
var yes = Resource.Yes;
var result = await page.DisplayActionSheet(message, Resource.No, yes);
return result == yes;
}
} }
public static class ModelExtensionHelper public static class ModelExtensionHelper

View File

@ -182,7 +182,7 @@ namespace Billing.UI
} }
else else
{ {
lastHeight = rowHeight + spacing; lastHeight += rowHeight + spacing;
} }
} }
lastSizeRequest = new SizeRequest(new Size(widthConstraint, lastHeight)); lastSizeRequest = new SizeRequest(new Size(widthConstraint, lastHeight));

View File

@ -11,6 +11,7 @@ namespace Billing.UI
public ItemSelectPage(IEnumerable<T> source) public ItemSelectPage(IEnumerable<T> source)
{ {
var iconConverter = new IconConverter();
var content = new ListView var content = new ListView
{ {
ItemsSource = source, ItemsSource = source,
@ -30,7 +31,7 @@ namespace Billing.UI
Aspect = Aspect.AspectFit, Aspect = Aspect.AspectFit,
VerticalOptions = LayoutOptions.Center VerticalOptions = LayoutOptions.Center
} }
.Binding(Image.SourceProperty, "Icon"), .Binding(Image.SourceProperty, "Icon", converter: iconConverter),
new Label new Label
{ {
@ -51,11 +52,12 @@ namespace Billing.UI
Content = content; Content = content;
} }
private void List_ItemTapped(object sender, ItemTappedEventArgs e) private async void List_ItemTapped(object sender, ItemTappedEventArgs e)
{ {
if (e.Item is T t) if (e.Item is T t)
{ {
ItemTapped?.Invoke(this, t); ItemTapped?.Invoke(this, t);
await Navigation.PopAsync();
} }
} }
} }

View File

@ -6,6 +6,8 @@ namespace Billing.UI
{ {
public class OptionEntry : Entry { } public class OptionEntry : Entry { }
public class OptionEditor : Editor { } public class OptionEditor : Editor { }
public class OptionDatePicker : DatePicker { }
public class OptionTimePicker : TimePicker { }
public abstract class OptionCell : ViewCell public abstract class OptionCell : ViewCell
{ {
@ -163,7 +165,6 @@ namespace Billing.UI
{ {
new Label new Label
{ {
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center VerticalOptions = LayoutOptions.Center
} }
.Binding(Label.TextProperty, nameof(Detail)) .Binding(Label.TextProperty, nameof(Detail))
@ -207,7 +208,6 @@ namespace Billing.UI
{ {
WidthRequest = 26, WidthRequest = 26,
HeightRequest = 20, HeightRequest = 20,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center,
Margin = new Thickness(6, 0) Margin = new Thickness(6, 0)
} }
@ -248,6 +248,46 @@ namespace Billing.UI
.Binding(Switch.IsToggledProperty, nameof(IsToggled), mode: BindingMode.TwoWay); .Binding(Switch.IsToggledProperty, nameof(IsToggled), mode: BindingMode.TwoWay);
} }
public class OptionDatePickerCell : OptionCell
{
public static readonly BindableProperty DateProperty = BindableProperty.Create(nameof(Date), typeof(DateTime), typeof(OptionDatePickerCell));
public DateTime Date
{
get => (DateTime)GetValue(DateProperty);
set => SetValue(DateProperty, value);
}
protected override View Content => new OptionDatePicker
{
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center
}
.Binding(DatePicker.DateProperty, nameof(Date), mode: BindingMode.TwoWay)
.DynamicResource(DatePicker.TextColorProperty, BaseTheme.TextColor)
.DynamicResource(VisualElement.BackgroundColorProperty, BaseTheme.OptionTintColor);
}
public class OptionTimePickerCell : OptionCell
{
public static readonly BindableProperty TimeProperty = BindableProperty.Create(nameof(Time), typeof(TimeSpan), typeof(OptionTimePickerCell));
public TimeSpan Time
{
get => (TimeSpan)GetValue(TimeProperty);
set => SetValue(TimeProperty, value);
}
protected override View Content => new OptionTimePicker
{
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center
}
.Binding(TimePicker.TimeProperty, nameof(Time), mode: BindingMode.TwoWay)
.DynamicResource(TimePicker.TextColorProperty, BaseTheme.TextColor)
.DynamicResource(VisualElement.BackgroundColorProperty, BaseTheme.OptionTintColor);
}
public class OptionEntryCell : OptionCell public class OptionEntryCell : OptionCell
{ {
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(OptionEntryCell)); public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(OptionEntryCell));

View File

@ -13,7 +13,6 @@
<ContentPage.Resources> <ContentPage.Resources>
<ui:MoneyConverter x:Key="moneyConverter"/> <ui:MoneyConverter x:Key="moneyConverter"/>
<ui:MoneyConverter x:Key="money2Converter" MarkVisible="False"/>
<ui:AccountCategoryConverter x:Key="categoryConverter"/> <ui:AccountCategoryConverter x:Key="categoryConverter"/>
<ui:IconConverter x:Key="iconConverter"/> <ui:IconConverter x:Key="iconConverter"/>
</ContentPage.Resources> </ContentPage.Resources>
@ -34,7 +33,7 @@
Text="{Binding Balance, Converter={StaticResource moneyConverter}}"/> Text="{Binding Balance, Converter={StaticResource moneyConverter}}"/>
</StackLayout> </StackLayout>
<Grid Grid.Column="1" Margin="20, 0" VerticalOptions="Center" HorizontalOptions="End" <Grid Grid.Column="1" Margin="20, 0" VerticalOptions="Center" HorizontalOptions="End"
ColumnDefinitions="Auto, Auto" RowDefinitions="Auto, Auto"> ColumnDefinitions="Auto, Auto" RowDefinitions="Auto, Auto" IsVisible="False">
<Label FontSize="Small" HorizontalOptions="End" Text="{r:Text Assets}"/> <Label FontSize="Small" HorizontalOptions="End" Text="{r:Text Assets}"/>
<Label Grid.Column="1" FontSize="Small" Margin="10, 0, 0, 0" HorizontalOptions="End" <Label Grid.Column="1" FontSize="Small" Margin="10, 0, 0, 0" HorizontalOptions="End"
Text="{Binding Asset, Converter={StaticResource moneyConverter}}"/> Text="{Binding Asset, Converter={StaticResource moneyConverter}}"/>
@ -44,20 +43,22 @@
Text="{Binding Liability, Converter={StaticResource moneyConverter}}"/> Text="{Binding Liability, Converter={StaticResource moneyConverter}}"/>
</Grid> </Grid>
</Grid> </Grid>
<ui:GroupStackLayout x:Name="groupLayout" ItemsSource="{Binding Accounts}" Margin="0, 10, 0, 0"> <ui:GroupStackLayout x:Name="groupLayout" ItemsSource="{Binding Accounts}" Margin="0, 10, 0, 0" GroupHeight="40">
<ui:GroupStackLayout.GroupHeaderTemplate> <ui:GroupStackLayout.GroupHeaderTemplate>
<DataTemplate x:DataType="v:AccountGrouping"> <DataTemplate x:DataType="v:AccountGrouping">
<StackLayout Orientation="Horizontal" Padding="10, 0"> <StackLayout Orientation="Horizontal" Padding="10, 0" VerticalOptions="End">
<Label Text="{Binding Key, Converter={StaticResource categoryConverter}}" <Label Text="{Binding Key, Converter={StaticResource categoryConverter}}"
TextColor="{DynamicResource SecondaryTextColor}"/> TextColor="{DynamicResource SecondaryTextColor}"/>
<Label Text="{Binding Balance, Converter={StaticResource money2Converter}}" <Label Text="{Binding Balance, Converter={StaticResource moneyConverter}}"
Margin="10, 0" TextColor="{DynamicResource SecondaryTextColor}"/> Margin="10, 0" TextColor="{DynamicResource SecondaryTextColor}"/>
</StackLayout> </StackLayout>
</DataTemplate> </DataTemplate>
</ui:GroupStackLayout.GroupHeaderTemplate> </ui:GroupStackLayout.GroupHeaderTemplate>
<ui:GroupStackLayout.ItemTemplate> <ui:GroupStackLayout.ItemTemplate>
<DataTemplate x:DataType="m:Account"> <DataTemplate x:DataType="m:Account">
<Grid Padding="20, 0, 10, 0" ColumnSpacing="10" ColumnDefinitions="Auto, *, Auto, Auto"> <ui:LongPressGrid Padding="20, 0, 10, 0" ColumnSpacing="10" ColumnDefinitions="Auto, *, Auto, Auto"
LongCommand="{Binding DeleteAccount, Source={x:Reference accountPage}}"
LongCommandParameter="{Binding .}">
<Grid.GestureRecognizers> <Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EditAccount, Source={x:Reference accountPage}}" <TapGestureRecognizer Command="{Binding EditAccount, Source={x:Reference accountPage}}"
CommandParameter="{Binding .}"/> CommandParameter="{Binding .}"/>
@ -67,11 +68,11 @@
<Label Grid.Column="1" Text="{Binding Name}" TextColor="{DynamicResource TextColor}" <Label Grid.Column="1" Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
HorizontalOptions="FillAndExpand" VerticalOptions="Center" HorizontalOptions="FillAndExpand" VerticalOptions="Center"
FontSize="Default" FontAttributes="Bold"/> FontSize="Default" FontAttributes="Bold"/>
<Label Grid.Column="2" Text="{Binding Balance, Converter={StaticResource money2Converter}}" <Label Grid.Column="2" Text="{Binding Balance, Converter={StaticResource moneyConverter}}"
TextColor="{DynamicResource SecondaryTextColor}" TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center"/> VerticalOptions="Center"/>
<ui:TintImage Grid.Column="3" Source="right.png" HeightRequest="20"/> <ui:TintImage Grid.Column="3" Source="right.png" HeightRequest="20"/>
</Grid> </ui:LongPressGrid>
</DataTemplate> </DataTemplate>
</ui:GroupStackLayout.ItemTemplate> </ui:GroupStackLayout.ItemTemplate>
</ui:GroupStackLayout> </ui:GroupStackLayout>

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Billing.Languages;
using Billing.Models; using Billing.Models;
using Billing.UI; using Billing.UI;
using Xamarin.Forms; using Xamarin.Forms;
@ -20,6 +21,7 @@ namespace Billing.Views
public List<AccountGrouping> Accounts => (List<AccountGrouping>)GetValue(AccountsProperty); public List<AccountGrouping> Accounts => (List<AccountGrouping>)GetValue(AccountsProperty);
public Command EditAccount { get; } public Command EditAccount { get; }
public Command DeleteAccount { get; }
private readonly List<AccountGrouping> accounts = new(); private readonly List<AccountGrouping> accounts = new();
private bool initialized; private bool initialized;
@ -27,6 +29,7 @@ namespace Billing.Views
public AccountPage() public AccountPage()
{ {
EditAccount = new Command(OnEditAccount); EditAccount = new Command(OnEditAccount);
DeleteAccount = new Command(OnDeleteAccount);
SetValue(AccountsProperty, accounts); SetValue(AccountsProperty, accounts);
InitializeComponent(); InitializeComponent();
@ -34,7 +37,7 @@ namespace Billing.Views
protected override void OnAppearing() protected override void OnAppearing()
{ {
if (!initialized) if (!initialized || accounts.Count == 0)
{ {
initialized = true; initialized = true;
accounts.Clear(); accounts.Clear();
@ -52,6 +55,12 @@ namespace Billing.Views
} }
} }
groupLayout.Refresh(accounts); groupLayout.Refresh(accounts);
RefreshBalance();
}
private void RefreshBalance()
{
SetValue(BalanceProperty, App.Accounts.Sum(a => a.Balance));
} }
private void AddToAccountGroup(Account account) private void AddToAccountGroup(Account account)
@ -94,6 +103,35 @@ namespace Billing.Views
} }
} }
private async void OnDeleteAccount(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is Account account)
{
var result = await this.ShowConfirm(Resource.ConfirmDeleteAccount);
if (result)
{
var group = accounts.FirstOrDefault(a => a.Key == account.Category);
if (group == null)
{
Helper.Error("account.delete", "unexpected deleting account, cannot find the current category");
return;
}
group.Remove(account);
groupLayout.Refresh(accounts);
RefreshBalance();
_ = Task.Run(App.WriteAccounts);
}
}
}
}
private void AccountChecked(object sender, AccountEventArgs e) private void AccountChecked(object sender, AccountEventArgs e)
{ {
if (e.Account.Id < 0) if (e.Account.Id < 0)
@ -102,6 +140,7 @@ namespace Billing.Views
AddToAccountGroup(e.Account); AddToAccountGroup(e.Account);
} }
groupLayout.Refresh(accounts); groupLayout.Refresh(accounts);
RefreshBalance();
Task.Run(App.WriteAccounts); Task.Run(App.WriteAccounts);
} }

View File

@ -152,10 +152,9 @@ namespace Billing.Views
} }
} }
private async void Category_ItemTapped(object sender, SelectItem<AccountCategory> e) private void Category_ItemTapped(object sender, SelectItem<AccountCategory> e)
{ {
Category = e.Value; Category = e.Value;
await Navigation.PopAsync();
} }
} }

View File

@ -39,9 +39,11 @@
<ui:OptionEntryCell Height="44" Icon="online.png" <ui:OptionEntryCell Height="44" Icon="online.png"
Title="{r:Text Store}" Title="{r:Text Store}"
Text="{Binding Store, Mode=TwoWay}"/> Text="{Binding Store, Mode=TwoWay}"/>
<ui:OptionSelectCell Height="44" Icon="bars.png" <ui:OptionDatePickerCell Height="44" Icon="bars.png"
Title="{r:Text CreatedTime}" Title="{r:Text CreatedTime}"
Detail="{Binding CreatedTime}"/> Date="{Binding CreatedDate, Mode=TwoWay}"/>
<ui:OptionTimePickerCell Height="44"
Time="{Binding CreatedTime, Mode=TwoWay}"/>
</TableSection> </TableSection>
<TableSection> <TableSection>
<TableSection.Title> <TableSection.Title>

View File

@ -15,7 +15,8 @@ namespace Billing.Views
private static readonly BindableProperty CategoryNameProperty = BindableProperty.Create(nameof(CategoryName), typeof(string), typeof(AddBillPage)); private static readonly BindableProperty CategoryNameProperty = BindableProperty.Create(nameof(CategoryName), typeof(string), typeof(AddBillPage));
private static readonly BindableProperty WalletNameProperty = BindableProperty.Create(nameof(WalletName), typeof(string), typeof(AddBillPage)); private static readonly BindableProperty WalletNameProperty = BindableProperty.Create(nameof(WalletName), typeof(string), typeof(AddBillPage));
private static readonly BindableProperty StoreProperty = BindableProperty.Create(nameof(Store), typeof(string), typeof(AddBillPage)); private static readonly BindableProperty StoreProperty = BindableProperty.Create(nameof(Store), typeof(string), typeof(AddBillPage));
private static readonly BindableProperty CreatedTimeProperty = BindableProperty.Create(nameof(CreatedTime), typeof(DateTime), typeof(AddBillPage)); private static readonly BindableProperty CreatedDateProperty = BindableProperty.Create(nameof(CreatedDate), typeof(DateTime), typeof(AddBillPage));
private static readonly BindableProperty CreatedTimeProperty = BindableProperty.Create(nameof(CreatedTime), typeof(TimeSpan), typeof(AddBillPage));
private static readonly BindableProperty NoteProperty = BindableProperty.Create(nameof(Note), typeof(string), typeof(AddBillPage)); private static readonly BindableProperty NoteProperty = BindableProperty.Create(nameof(Note), typeof(string), typeof(AddBillPage));
public string Amount public string Amount
@ -35,9 +36,14 @@ namespace Billing.Views
get => (string)GetValue(StoreProperty); get => (string)GetValue(StoreProperty);
set => SetValue(StoreProperty, value); set => SetValue(StoreProperty, value);
} }
public DateTime CreatedTime public DateTime CreatedDate
{ {
get => (DateTime)GetValue(CreatedTimeProperty); get => (DateTime)GetValue(CreatedDateProperty);
set => SetValue(CreatedDateProperty, value);
}
public TimeSpan CreatedTime
{
get => (TimeSpan)GetValue(CreatedTimeProperty);
set => SetValue(CreatedTimeProperty, value); set => SetValue(CreatedTimeProperty, value);
} }
public string Note public string Note
@ -86,14 +92,15 @@ namespace Billing.Views
{ {
if (bill != null) if (bill != null)
{ {
Amount = bill.Amount.ToString(CultureInfo.InvariantCulture); Amount = Math.Abs(bill.Amount).ToString(CultureInfo.InvariantCulture);
Name = bill.Name; Name = bill.Name;
walletId = bill.WalletId; walletId = bill.WalletId;
categoryId = bill.CategoryId; categoryId = bill.CategoryId;
SetValue(WalletNameProperty, App.Accounts.FirstOrDefault(a => a.Id == walletId)?.Name); SetValue(WalletNameProperty, App.Accounts.FirstOrDefault(a => a.Id == walletId)?.Name);
SetValue(CategoryNameProperty, App.Categories.FirstOrDefault(c => c.Id == categoryId)?.Name); SetValue(CategoryNameProperty, App.Categories.FirstOrDefault(c => c.Id == categoryId)?.Name);
Store = bill.Store; Store = bill.Store;
CreatedTime = bill.CreateTime; CreatedDate = bill.CreateTime.Date;
CreatedTime = bill.CreateTime.TimeOfDay;
Note = bill.Note; Note = bill.Note;
} }
else else
@ -104,7 +111,8 @@ namespace Billing.Views
var firstCategory = App.Categories.First(); var firstCategory = App.Categories.First();
categoryId = firstCategory.Id; categoryId = firstCategory.Id;
SetValue(CategoryNameProperty, firstCategory.Name); SetValue(CategoryNameProperty, firstCategory.Name);
CreatedTime = createDate.Date.Add(DateTime.Now.TimeOfDay); CreatedDate = createDate.Date;
CreatedTime = DateTime.Now.TimeOfDay;
} }
} }
@ -132,13 +140,18 @@ namespace Billing.Views
amount *= -1; amount *= -1;
} }
await Navigation.PopAsync(); await Navigation.PopAsync();
var name = Name;
if (string.IsNullOrWhiteSpace(name))
{
name = category.Name;
}
if (bill != null) if (bill != null)
{ {
bill.Amount = amount; bill.Amount = amount;
bill.Name = Name; bill.Name = name;
bill.CategoryId = categoryId; bill.CategoryId = categoryId;
bill.WalletId = walletId; bill.WalletId = walletId;
bill.CreateTime = CreatedTime; bill.CreateTime = CreatedDate.Date.Add(CreatedTime);
bill.Store = Store; bill.Store = Store;
bill.Note = Note; bill.Note = Note;
} }
@ -146,24 +159,66 @@ namespace Billing.Views
{ {
Id = -1, Id = -1,
Amount = amount, Amount = amount,
Name = Name, Name = name,
CategoryId = categoryId, CategoryId = categoryId,
WalletId = walletId, WalletId = walletId,
CreateTime = CreatedTime, CreateTime = CreatedDate.Date.Add(CreatedTime),
Store = Store, Store = Store,
Note = Note Note = Note
}); });
} }
} }
private void OnSelectCategory() private async void OnSelectCategory()
{ {
if (Tap.IsBusy)
}
private void OnSelectWallet()
{ {
return;
}
using (Tap.Start())
{
var source = App.Categories.Select(c => new SelectItem<int>
{
Value = c.Id,
Name = c.Name,
Icon = c.Icon
});
var page = new ItemSelectPage<SelectItem<int>>(source);
page.ItemTapped += Category_ItemTapped;
await Navigation.PushAsync(page);
}
}
private void Category_ItemTapped(object sender, SelectItem<int> category)
{
categoryId = category.Value;
SetValue(CategoryNameProperty, category.Name);
}
private async void OnSelectWallet()
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
var source = App.Accounts.Select(a => new SelectItem<int>
{
Value = a.Id,
Name = a.Name,
Icon = a.Icon
});
var page = new ItemSelectPage<SelectItem<int>>(source);
page.ItemTapped += Wallet_ItemTapped;
await Navigation.PushAsync(page);
}
}
private void Wallet_ItemTapped(object sender, SelectItem<int> account)
{
walletId = account.Value;
SetValue(WalletNameProperty, account.Name);
} }
} }
} }

View File

@ -12,9 +12,11 @@
<ContentPage.Resources> <ContentPage.Resources>
<ui:TitleDateConverter x:Key="titleDateConverter"/> <ui:TitleDateConverter x:Key="titleDateConverter"/>
<ui:MoneyConverter x:Key="moneyConverter" MarkVisible="False" Absolute="True"/> <ui:NegativeConverter x:Key="negativeConverter"/>
<ui:MoneyConverter x:Key="moneyConverter" Absolute="True"/>
<ui:MoneyConverter x:Key="moneyRawConverter"/>
<ui:BalanceColorConverter x:Key="colorConverter"/> <ui:BalanceColorConverter x:Key="colorConverter"/>
<ui:UIBillConverter x:Key="billConverter"/> <ui:TimeConverter x:Key="timeConverter"/>
<ui:IconConverter x:Key="iconConverter"/> <ui:IconConverter x:Key="iconConverter"/>
</ContentPage.Resources> </ContentPage.Resources>
@ -36,28 +38,51 @@
<Grid RowDefinitions="Auto, Auto, *"> <Grid RowDefinitions="Auto, Auto, *">
<ui:BillingDate x:Name="billingDate" SelectedDate="{Binding SelectedDate}" DateSelected="OnDateSelected"/> <ui:BillingDate x:Name="billingDate" SelectedDate="{Binding SelectedDate}" DateSelected="OnDateSelected"/>
<Grid Grid.Row="1" Padding="8" ColumnSpacing="8" ColumnDefinitions="Auto, *, Auto" <Grid Grid.Row="1" Padding="8" ColumnSpacing="8" ColumnDefinitions="*, Auto"
BackgroundColor="{DynamicResource PromptBackgroundColor}"> BackgroundColor="{DynamicResource PromptBackgroundColor}"
<ui:TintImage Source="bars.png" WidthRequest="23" HeightRequest="23"/> IsVisible="{Binding NoBills}">
<Label Grid.Column="1" Text="{r:Text NoRecords}" TextColor="{DynamicResource TextColor}" <Label Text="{r:Text NoRecords}" TextColor="{DynamicResource TextColor}"
VerticalOptions="Center"/> VerticalOptions="Center" FontSize="12"/>
<StackLayout Grid.Column="2" Orientation="Horizontal" Spacing="6"> <StackLayout Grid.Column="1" Orientation="Horizontal" Spacing="6">
<StackLayout.GestureRecognizers> <StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EditBilling}"/> <TapGestureRecognizer Command="{Binding EditBilling}"/>
</StackLayout.GestureRecognizers> </StackLayout.GestureRecognizers>
<Label Text="{r:Text TapToMemo}" TextColor="{DynamicResource PrimaryColor}" <Label Text="{r:Text TapToMemo}" TextColor="{DynamicResource PrimaryColor}"
VerticalOptions="Center"/> VerticalOptions="Center" FontSize="12"/>
<ui:TintImage Source="right.png" WidthRequest="24" HeightRequest="24"/> <ui:TintImage Source="right.png" WidthRequest="24" HeightRequest="24"/>
</StackLayout> </StackLayout>
</Grid> </Grid>
<Grid Grid.Row="1" Padding="8" ColumnSpacing="8" ColumnDefinitions="*, Auto" HeightRequest="24"
BackgroundColor="{DynamicResource PromptBackgroundColor}"
IsVisible="{Binding NoBills, Converter={StaticResource negativeConverter}}">
<StackLayout Grid.Column="1" Orientation="Horizontal" Spacing="6">
<Label Text="{r:Text Income}" TextColor="{DynamicResource GreenColor}"
VerticalOptions="Center" FontSize="12"/>
<Label Text="{Binding Income, Converter={StaticResource moneyConverter}}"
TextColor="{DynamicResource TextColor}"
VerticalOptions="Center" FontSize="12"/>
<Label Text="{r:Text Spending}" TextColor="{DynamicResource RedColor}"
VerticalOptions="Center" FontSize="12" Margin="10, 0, 0, 0"/>
<Label Text="{Binding Spending, Converter={StaticResource moneyConverter}}"
TextColor="{DynamicResource TextColor}"
VerticalOptions="Center" FontSize="12"/>
<Label Text="{r:Text Balance}"
VerticalOptions="Center" FontSize="12" Margin="10, 0, 0, 0"/>
<Label Text="{Binding Balance, Converter={StaticResource moneyRawConverter}}"
TextColor="{DynamicResource TextColor}"
VerticalOptions="Center" FontSize="12"/>
</StackLayout>
</Grid>
<!-- bill list --> <!-- bill list -->
<ScrollView Grid.Row="2"> <ScrollView Grid.Row="2">
<ui:GroupStackLayout x:Name="billsLayout" ItemsSource="{Binding Bills}" <ui:GroupStackLayout x:Name="billsLayout" ItemsSource="{Binding Bills}"
Margin="0, 10, 0, 0" RowHeight="50"> Margin="0, 10, 0, 0" RowHeight="50">
<ui:GroupStackLayout.ItemTemplate> <ui:GroupStackLayout.ItemTemplate>
<DataTemplate x:DataType="v:UIBill"> <DataTemplate x:DataType="v:UIBill">
<Grid Padding="20, 0, 10, 0" ColumnSpacing="10" <ui:LongPressGrid Padding="20, 0" ColumnSpacing="10"
ColumnDefinitions="Auto, *, Auto" RowDefinitions="Auto, Auto"> ColumnDefinitions="Auto, *, Auto" RowDefinitions="Auto, Auto"
LongCommand="{Binding DeleteBilling, Source={x:Reference billPage}}"
LongCommandParameter="{Binding .}">
<Grid.GestureRecognizers> <Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EditBilling, Source={x:Reference billPage}}" <TapGestureRecognizer Command="{Binding EditBilling, Source={x:Reference billPage}}"
CommandParameter="{Binding .}"/> CommandParameter="{Binding .}"/>
@ -70,10 +95,13 @@
<Label Grid.Column="2" Text="{Binding Amount, Converter={StaticResource moneyConverter}}" <Label Grid.Column="2" Text="{Binding Amount, Converter={StaticResource moneyConverter}}"
TextColor="{Binding Amount, Converter={StaticResource colorConverter}}" TextColor="{Binding Amount, Converter={StaticResource colorConverter}}"
VerticalOptions="Center"/> VerticalOptions="Center"/>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding ., Converter={StaticResource billConverter}}" <StackLayout Grid.Row="1" Grid.Column="1" Spacing="6" Orientation="Horizontal">
FontSize="10" <Label Text="{Binding DateCreation, Converter={StaticResource timeConverter}}"
TextColor="{DynamicResource SecondaryTextColor}"/> FontSize="10" TextColor="{DynamicResource SecondaryTextColor}"/>
</Grid> <Label Text="{Binding Wallet}"
FontSize="10" TextColor="{DynamicResource SecondaryTextColor}"/>
</StackLayout>
</ui:LongPressGrid>
</DataTemplate> </DataTemplate>
</ui:GroupStackLayout.ItemTemplate> </ui:GroupStackLayout.ItemTemplate>
</ui:GroupStackLayout> </ui:GroupStackLayout>

View File

@ -13,6 +13,10 @@ namespace Billing.Views
{ {
private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage)); private static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(nameof(SelectedDate), typeof(DateTime), typeof(BillPage));
private static readonly BindableProperty BillsProperty = BindableProperty.Create(nameof(Bills), typeof(List<UIBill>), typeof(BillPage)); private static readonly BindableProperty BillsProperty = BindableProperty.Create(nameof(Bills), typeof(List<UIBill>), typeof(BillPage));
private static readonly BindableProperty NoBillsProperty = BindableProperty.Create(nameof(NoBills), typeof(bool), typeof(BillPage));
private static readonly BindableProperty IncomeProperty = BindableProperty.Create(nameof(Income), typeof(decimal), typeof(BillPage));
private static readonly BindableProperty SpendingProperty = BindableProperty.Create(nameof(Spending), typeof(decimal), typeof(BillPage));
private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(BillPage));
public DateTime SelectedDate public DateTime SelectedDate
{ {
@ -24,12 +28,18 @@ namespace Billing.Views
get => (List<UIBill>)GetValue(BillsProperty); get => (List<UIBill>)GetValue(BillsProperty);
set => SetValue(BillsProperty, value); set => SetValue(BillsProperty, value);
} }
public bool NoBills => (bool)GetValue(NoBillsProperty);
public decimal Income => (decimal)GetValue(IncomeProperty);
public decimal Spending => (decimal)GetValue(SpendingProperty);
public decimal Balance => (decimal)GetValue(BalanceProperty);
public Command EditBilling { get; } public Command EditBilling { get; }
public Command DeleteBilling { get; }
public BillPage() public BillPage()
{ {
EditBilling = new Command(OnEditBilling); EditBilling = new Command(OnEditBilling);
DeleteBilling = new Command(OnDeleteBilling);
InitializeComponent(); InitializeComponent();
@ -45,6 +55,17 @@ namespace Billing.Views
b.CreateTime.Month == e.Date.Month && b.CreateTime.Month == e.Date.Month &&
b.CreateTime.Day == e.Date.Day); b.CreateTime.Day == e.Date.Day);
Bills = new List<UIBill>(bills.Select(b => WrapBill(b))); Bills = new List<UIBill>(bills.Select(b => WrapBill(b)));
RefreshBalance(Bills);
}
private void RefreshBalance(List<UIBill> bills)
{
SetValue(NoBillsProperty, bills.Count == 0);
var income = bills.Where(b => b.Amount > 0).Sum(b => b.Amount);
var spending = -bills.Where(b => b.Amount < 0).Sum(b => b.Amount);
SetValue(IncomeProperty, income);
SetValue(SpendingProperty, spending);
SetValue(BalanceProperty, income - spending);
} }
private UIBill WrapBill(Bill b) private UIBill WrapBill(Bill b)
@ -101,6 +122,31 @@ namespace Billing.Views
} }
} }
private async void OnDeleteBilling(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is UIBill bill)
{
var result = await this.ShowConfirm(Resource.ConfirmDeleteBill);
if (result)
{
var bills = Bills;
bills.Remove(bill);
App.Bills.Remove(bill.Bill);
billsLayout.Refresh(bills);
RefreshBalance(bills);
_ = Task.Run(App.WriteBills);
}
}
}
}
private void OnBillChecked(object sender, Bill e) private void OnBillChecked(object sender, Bill e)
{ {
if (e.Id < 0) if (e.Id < 0)
@ -127,6 +173,7 @@ namespace Billing.Views
UpdateBill(bill); UpdateBill(bill);
} }
} }
RefreshBalance(Bills);
Task.Run(App.WriteBills); Task.Run(App.WriteBills);
} }

View File

@ -40,7 +40,11 @@ namespace Billing.Views
new() { Icon = BaseModel.ICON_DEFAULT }, new() { Icon = BaseModel.ICON_DEFAULT },
new() { Icon = "wallet" }, new() { Icon = "wallet" },
new() { Icon = "creditcard" }, new() { Icon = "creditcard" },
new() { Icon = "debitcard" } new() { Icon = "debitcard" },
new() { Icon = "cmb" },
new() { Icon = "rcb" },
new() { Icon = "yuebao" },
new() { Icon = "zhaozhaoying" }
}; };
source.AddRange(IconConverter.IconPreset.Select(icon => new BillingIcon { Icon = $"#brand#{icon.Key}" })); source.AddRange(IconConverter.IconPreset.Select(icon => new BillingIcon { Icon = $"#brand#{icon.Key}" }));
foreach (var icon in source) foreach (var icon in source)

View File

@ -2,12 +2,27 @@
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms" <ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:r="clr-namespace:Billing.Languages" xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI" xmlns:ui="clr-namespace:Billing.UI"
xmlns:v="clr-namespace:Billing.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.SettingPage" x:Class="Billing.Views.SettingPage"
Title="{r:Text Settings}"> x:Name="settingPage"
<StackLayout> x:DataType="v:SettingPage"
<Label Text="Welcome to Settings Page!" Title="{r:Text Settings}"
VerticalOptions="CenterAndExpand" BindingContext="{x:Reference settingPage}">
HorizontalOptions="CenterAndExpand" />
</StackLayout> <TableView Intent="Settings" RowHeight="44">
<TableSection Title="{r:Text About}">
<ui:OptionTextCell Title="{r:Text Version}"
Detail="{Binding Version}"/>
</TableSection>
<TableSection Title="{r:Text Preference}">
<ui:OptionEntryCell Title="{r:Text PrimaryColor}"
Text="{Binding Red, Mode=TwoWay}"
Keyboard="Numeric"/>
<ui:OptionEntryCell Text="{Binding Green, Mode=TwoWay}"
Keyboard="Numeric"/>
<ui:OptionEntryCell Text="{Binding Blue, Mode=TwoWay}"
Keyboard="Numeric"/>
</TableSection>
</TableView>
</ui:BillingPage> </ui:BillingPage>

View File

@ -1,12 +1,61 @@
using Billing.Themes;
using Billing.UI; using Billing.UI;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Billing.Views namespace Billing.Views
{ {
public partial class SettingPage : BillingPage public partial class SettingPage : BillingPage
{ {
private static readonly BindableProperty VersionProperty = BindableProperty.Create(nameof(Version), typeof(string), typeof(SettingPage));
private static readonly BindableProperty RedProperty = BindableProperty.Create(nameof(Red), typeof(byte), typeof(SettingPage));
private static readonly BindableProperty GreenProperty = BindableProperty.Create(nameof(Green), typeof(byte), typeof(SettingPage));
private static readonly BindableProperty BlueProperty = BindableProperty.Create(nameof(Blue), typeof(byte), typeof(SettingPage));
public string Version => (string)GetValue(VersionProperty);
public byte Red
{
get => (byte)GetValue(RedProperty);
set => SetValue(RedProperty, value);
}
public byte Green
{
get => (byte)GetValue(GreenProperty);
set => SetValue(GreenProperty, value);
}
public byte Blue
{
get => (byte)GetValue(BlueProperty);
set => SetValue(BlueProperty, value);
}
public SettingPage() public SettingPage()
{ {
InitializeComponent(); InitializeComponent();
var (main, build) = Definition.GetVersion();
SetValue(VersionProperty, $"{main} ({build})");
}
protected override void OnAppearing()
{
base.OnAppearing();
//SetValue(VersionProperty, $"{AppInfo.VersionString} ({AppInfo.BuildString})");
var colorString = Preferences.Get(Definition.PrimaryColorKey, "#183153");
var color = Color.FromHex(colorString);
Red = (byte)(color.R * 255);
Green = (byte)(color.G * 255);
Blue = (byte)(color.B * 255);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
var color = Color.FromRgb(Red, Green, Blue);
Preferences.Set(Definition.PrimaryColorKey, color.ToHex());
Light.Instance.RefreshColor(color);
} }
} }
} }

View File

@ -74,7 +74,10 @@
<Compile Include="MainActivity.cs" /> <Compile Include="MainActivity.cs" />
<Compile Include="PlatformCulture.cs" /> <Compile Include="PlatformCulture.cs" />
<Compile Include="Renderers\LongPressButtonRenderer.cs" /> <Compile Include="Renderers\LongPressButtonRenderer.cs" />
<Compile Include="Renderers\LongPressGridRenderer.cs" />
<Compile Include="Renderers\OptionDatePickerRenderer.cs" />
<Compile Include="Renderers\OptionEntryRenderer.cs" /> <Compile Include="Renderers\OptionEntryRenderer.cs" />
<Compile Include="Renderers\OptionTimePickerRenderer.cs" />
<Compile Include="Renderers\TintImageRenderer.cs" /> <Compile Include="Renderers\TintImageRenderer.cs" />
<Compile Include="Resources\Resource.designer.cs" /> <Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@ -199,6 +202,138 @@
<AndroidResource Include="Resources\drawable-xxhdpi\wallet.png" /> <AndroidResource Include="Resources\drawable-xxhdpi\wallet.png" />
<AndroidResource Include="Resources\drawable-xxhdpi\yuan.png" /> <AndroidResource Include="Resources\drawable-xxhdpi\yuan.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\cmb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\rcb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\yuebao.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\zhaozhaoying.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\cmb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\rcb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\yuebao.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\zhaozhaoying.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\cmb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\rcb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\yuebao.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\zhaozhaoying.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\cmb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\rcb.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\yuebao.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\zhaozhaoying.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\clothes.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\daily.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\drink.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\food.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\learn.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\medical.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\trans.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\clothes.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\daily.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\drink.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\food.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\learn.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\medical.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\trans.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\clothes.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\daily.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\drink.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\food.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\learn.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\medical.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\trans.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\clothes.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\daily.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\drink.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\food.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\learn.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\medical.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xxhdpi\trans.png" />
</ItemGroup>
<Import Project="..\..\Billing.Shared\Billing.Shared.projitems" Label="Shared" /> <Import Project="..\..\Billing.Shared\Billing.Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -2,6 +2,17 @@
{ {
public static partial class Definition public static partial class Definition
{ {
public static partial (string main, long build) GetVersion()
{
var context = Android.App.Application.Context;
var manager = context.PackageManager;
var info = manager.GetPackageInfo(context.PackageName, 0);
string version = info.VersionName;
long build = info.LongVersionCode;
return (version, build);
}
public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular.ttf#CascadiaCode-Regular"; public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular.ttf#CascadiaCode-Regular";
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold.ttf#CascadiaCode-Bold"; public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold.ttf#CascadiaCode-Bold";
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular.ttf#RobotoCondensed-Regular"; public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular.ttf#RobotoCondensed-Regular";

View File

@ -10,6 +10,7 @@ namespace Billing.Droid
Icon = "@mipmap/icon", Icon = "@mipmap/icon",
RoundIcon = "@mipmap/icon_round", RoundIcon = "@mipmap/icon_round",
Theme = "@style/MainTheme", Theme = "@style/MainTheme",
ScreenOrientation = ScreenOrientation.Portrait,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)] ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{ {

View File

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

View File

@ -17,11 +17,19 @@ namespace Billing.Droid.Renderers
{ {
base.OnElementChanged(e); base.OnElementChanged(e);
if (Element is LongPressButton && Control != null) if (Control != null)
{
if (e.OldElement != null)
{
Control.LongClick -= Control_LongClick;
}
if (e.NewElement != null)
{ {
Control.LongClick += Control_LongClick; Control.LongClick += Control_LongClick;
} }
} }
}
private void Control_LongClick(object sender, LongClickEventArgs e) private void Control_LongClick(object sender, LongClickEventArgs e)
{ {

View File

@ -0,0 +1,63 @@
using Android.Content;
using Billing.Droid.Renderers;
using Billing.UI;
using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(LongPressGrid), typeof(LongPressGridRenderer))]
namespace Billing.Droid.Renderers
{
public class LongPressGridRenderer : ViewRenderer<LongPressGrid, Android.Views.View>
{
public LongPressGridRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<LongPressGrid> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
if (Control != null)
{
Control.LongClick -= Grid_LongClick;
}
}
if (e.NewElement != null)
{
if (Control == null)
{
var view = new Android.Views.View(Context)
{
Clickable = true,
LongClickable = true
};
view.Click += Grid_Click;
view.LongClick += Grid_LongClick;
SetNativeControl(view);
}
}
}
private void Grid_Click(object sender, EventArgs e)
{
var gesture = Element?.GestureRecognizers?.FirstOrDefault(g => g is TapGestureRecognizer);
if (gesture is TapGestureRecognizer tap)
{
if (tap.Command != null && tap.Command.CanExecute(tap.CommandParameter))
{
tap.Command.Execute(tap.CommandParameter);
}
}
}
private void Grid_LongClick(object sender, LongClickEventArgs e)
{
Element.TriggerLongPress();
}
}
}

View File

@ -0,0 +1,28 @@
using Android.Content;
using Android.Graphics.Drawables;
using Billing.Droid.Renderers;
using Billing.UI;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(OptionDatePicker), typeof(OptionDatePickerRenderer))]
namespace Billing.Droid.Renderers
{
public class OptionDatePickerRenderer : DatePickerRenderer
{
public OptionDatePickerRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var drawable = new ColorDrawable(e.NewElement.BackgroundColor.ToAndroid());
Control.SetBackground(drawable);
}
}
}
}

View File

@ -0,0 +1,28 @@
using Android.Content;
using Android.Graphics.Drawables;
using Billing.Droid.Renderers;
using Billing.UI;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(OptionTimePicker), typeof(OptionTimePickerRenderer))]
namespace Billing.Droid.Renderers
{
public class OptionTimePickerRenderer : TimePickerRenderer
{
public OptionTimePickerRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var drawable = new ColorDrawable(e.NewElement.BackgroundColor.ToAndroid());
Control.SetBackground(drawable);
}
}
}
}

View File

@ -16532,208 +16532,241 @@ namespace Billing.Droid
public const int check = 2131165282; public const int check = 2131165282;
// aapt resource value: 0x7F070063 // aapt resource value: 0x7F070063
public const int creditcard = 2131165283; public const int clothes = 2131165283;
// aapt resource value: 0x7F070064 // aapt resource value: 0x7F070064
public const int debitcard = 2131165284; public const int cmb = 2131165284;
// aapt resource value: 0x7F070065 // aapt resource value: 0x7F070065
public const int design_fab_background = 2131165285; public const int creditcard = 2131165285;
// aapt resource value: 0x7F070066 // aapt resource value: 0x7F070066
public const int design_ic_visibility = 2131165286; public const int daily = 2131165286;
// aapt resource value: 0x7F070067 // aapt resource value: 0x7F070067
public const int design_ic_visibility_off = 2131165287; public const int debitcard = 2131165287;
// aapt resource value: 0x7F070068 // aapt resource value: 0x7F070068
public const int design_password_eye = 2131165288; public const int design_fab_background = 2131165288;
// aapt resource value: 0x7F070069 // aapt resource value: 0x7F070069
public const int design_snackbar_background = 2131165289; public const int design_ic_visibility = 2131165289;
// aapt resource value: 0x7F07006A // aapt resource value: 0x7F07006A
public const int dollar = 2131165290; public const int design_ic_visibility_off = 2131165290;
// aapt resource value: 0x7F07006B // aapt resource value: 0x7F07006B
public const int face = 2131165291; public const int design_password_eye = 2131165291;
// aapt resource value: 0x7F070074
public const int icon_foreground = 2131165300;
// aapt resource value: 0x7F07006C // aapt resource value: 0x7F07006C
public const int ic_arrow_down_24dp = 2131165292; public const int design_snackbar_background = 2131165292;
// aapt resource value: 0x7F07006D // aapt resource value: 0x7F07006D
public const int ic_clock_black_24dp = 2131165293; public const int dollar = 2131165293;
// aapt resource value: 0x7F07006E // aapt resource value: 0x7F07006E
public const int ic_default = 2131165294; public const int drink = 2131165294;
// aapt resource value: 0x7F07006F // aapt resource value: 0x7F07006F
public const int ic_keyboard_black_24dp = 2131165295; public const int face = 2131165295;
// aapt resource value: 0x7F070070 // aapt resource value: 0x7F070070
public const int ic_mtrl_checked_circle = 2131165296; public const int food = 2131165296;
// aapt resource value: 0x7F070071
public const int ic_mtrl_chip_checked_black = 2131165297;
// aapt resource value: 0x7F070072
public const int ic_mtrl_chip_checked_circle = 2131165298;
// aapt resource value: 0x7F070073
public const int ic_mtrl_chip_close_circle = 2131165299;
// aapt resource value: 0x7F070075
public const int material_cursor_drawable = 2131165301;
// aapt resource value: 0x7F070076
public const int material_ic_calendar_black_24dp = 2131165302;
// aapt resource value: 0x7F070077
public const int material_ic_clear_black_24dp = 2131165303;
// aapt resource value: 0x7F070078
public const int material_ic_edit_black_24dp = 2131165304;
// aapt resource value: 0x7F070079 // aapt resource value: 0x7F070079
public const int material_ic_keyboard_arrow_left_black_24dp = 2131165305; public const int icon_foreground = 2131165305;
// aapt resource value: 0x7F070071
public const int ic_arrow_down_24dp = 2131165297;
// aapt resource value: 0x7F070072
public const int ic_clock_black_24dp = 2131165298;
// aapt resource value: 0x7F070073
public const int ic_default = 2131165299;
// aapt resource value: 0x7F070074
public const int ic_keyboard_black_24dp = 2131165300;
// aapt resource value: 0x7F070075
public const int ic_mtrl_checked_circle = 2131165301;
// aapt resource value: 0x7F070076
public const int ic_mtrl_chip_checked_black = 2131165302;
// aapt resource value: 0x7F070077
public const int ic_mtrl_chip_checked_circle = 2131165303;
// aapt resource value: 0x7F070078
public const int ic_mtrl_chip_close_circle = 2131165304;
// aapt resource value: 0x7F07007A // aapt resource value: 0x7F07007A
public const int material_ic_keyboard_arrow_next_black_24dp = 2131165306; public const int learn = 2131165306;
// aapt resource value: 0x7F07007B // aapt resource value: 0x7F07007B
public const int material_ic_keyboard_arrow_previous_black_24dp = 2131165307; public const int material_cursor_drawable = 2131165307;
// aapt resource value: 0x7F07007C // aapt resource value: 0x7F07007C
public const int material_ic_keyboard_arrow_right_black_24dp = 2131165308; public const int material_ic_calendar_black_24dp = 2131165308;
// aapt resource value: 0x7F07007D // aapt resource value: 0x7F07007D
public const int material_ic_menu_arrow_down_black_24dp = 2131165309; public const int material_ic_clear_black_24dp = 2131165309;
// aapt resource value: 0x7F07007E // aapt resource value: 0x7F07007E
public const int material_ic_menu_arrow_up_black_24dp = 2131165310; public const int material_ic_edit_black_24dp = 2131165310;
// aapt resource value: 0x7F07007F // aapt resource value: 0x7F07007F
public const int mtrl_dialog_background = 2131165311; public const int material_ic_keyboard_arrow_left_black_24dp = 2131165311;
// aapt resource value: 0x7F070080 // aapt resource value: 0x7F070080
public const int mtrl_dropdown_arrow = 2131165312; public const int material_ic_keyboard_arrow_next_black_24dp = 2131165312;
// aapt resource value: 0x7F070081 // aapt resource value: 0x7F070081
public const int mtrl_ic_arrow_drop_down = 2131165313; public const int material_ic_keyboard_arrow_previous_black_24dp = 2131165313;
// aapt resource value: 0x7F070082 // aapt resource value: 0x7F070082
public const int mtrl_ic_arrow_drop_up = 2131165314; public const int material_ic_keyboard_arrow_right_black_24dp = 2131165314;
// aapt resource value: 0x7F070083 // aapt resource value: 0x7F070083
public const int mtrl_ic_cancel = 2131165315; public const int material_ic_menu_arrow_down_black_24dp = 2131165315;
// aapt resource value: 0x7F070084 // aapt resource value: 0x7F070084
public const int mtrl_ic_error = 2131165316; public const int material_ic_menu_arrow_up_black_24dp = 2131165316;
// aapt resource value: 0x7F070085 // aapt resource value: 0x7F070085
public const int mtrl_navigation_bar_item_background = 2131165317; public const int medical = 2131165317;
// aapt resource value: 0x7F070086 // aapt resource value: 0x7F070086
public const int mtrl_popupmenu_background = 2131165318; public const int mtrl_dialog_background = 2131165318;
// aapt resource value: 0x7F070087 // aapt resource value: 0x7F070087
public const int mtrl_popupmenu_background_dark = 2131165319; public const int mtrl_dropdown_arrow = 2131165319;
// aapt resource value: 0x7F070088 // aapt resource value: 0x7F070088
public const int mtrl_tabs_default_indicator = 2131165320; public const int mtrl_ic_arrow_drop_down = 2131165320;
// aapt resource value: 0x7F070089 // aapt resource value: 0x7F070089
public const int navigation_empty_icon = 2131165321; public const int mtrl_ic_arrow_drop_up = 2131165321;
// aapt resource value: 0x7F07008A // aapt resource value: 0x7F07008A
public const int note = 2131165322; public const int mtrl_ic_cancel = 2131165322;
// aapt resource value: 0x7F07008B // aapt resource value: 0x7F07008B
public const int notification_action_background = 2131165323; public const int mtrl_ic_error = 2131165323;
// aapt resource value: 0x7F07008C // aapt resource value: 0x7F07008C
public const int notification_bg = 2131165324; public const int mtrl_navigation_bar_item_background = 2131165324;
// aapt resource value: 0x7F07008D // aapt resource value: 0x7F07008D
public const int notification_bg_low = 2131165325; public const int mtrl_popupmenu_background = 2131165325;
// aapt resource value: 0x7F07008E // aapt resource value: 0x7F07008E
public const int notification_bg_low_normal = 2131165326; public const int mtrl_popupmenu_background_dark = 2131165326;
// aapt resource value: 0x7F07008F // aapt resource value: 0x7F07008F
public const int notification_bg_low_pressed = 2131165327; public const int mtrl_tabs_default_indicator = 2131165327;
// aapt resource value: 0x7F070090 // aapt resource value: 0x7F070090
public const int notification_bg_normal = 2131165328; public const int navigation_empty_icon = 2131165328;
// aapt resource value: 0x7F070091 // aapt resource value: 0x7F070091
public const int notification_bg_normal_pressed = 2131165329; public const int note = 2131165329;
// aapt resource value: 0x7F070092 // aapt resource value: 0x7F070092
public const int notification_icon_background = 2131165330; public const int notification_action_background = 2131165330;
// aapt resource value: 0x7F070093 // aapt resource value: 0x7F070093
public const int notification_template_icon_bg = 2131165331; public const int notification_bg = 2131165331;
// aapt resource value: 0x7F070094 // aapt resource value: 0x7F070094
public const int notification_template_icon_low_bg = 2131165332; public const int notification_bg_low = 2131165332;
// aapt resource value: 0x7F070095 // aapt resource value: 0x7F070095
public const int notification_tile_bg = 2131165333; public const int notification_bg_low_normal = 2131165333;
// aapt resource value: 0x7F070096 // aapt resource value: 0x7F070096
public const int notify_panel_notification_icon_bg = 2131165334; public const int notification_bg_low_pressed = 2131165334;
// aapt resource value: 0x7F070097 // aapt resource value: 0x7F070097
public const int online = 2131165335; public const int notification_bg_normal = 2131165335;
// aapt resource value: 0x7F070098 // aapt resource value: 0x7F070098
public const int pencil = 2131165336; public const int notification_bg_normal_pressed = 2131165336;
// aapt resource value: 0x7F070099 // aapt resource value: 0x7F070099
public const int plus = 2131165337; public const int notification_icon_background = 2131165337;
// aapt resource value: 0x7F07009A // aapt resource value: 0x7F07009A
public const int preference_list_divider_material = 2131165338; public const int notification_template_icon_bg = 2131165338;
// aapt resource value: 0x7F07009B // aapt resource value: 0x7F07009B
public const int project = 2131165339; public const int notification_template_icon_low_bg = 2131165339;
// aapt resource value: 0x7F07009C // aapt resource value: 0x7F07009C
public const int right = 2131165340; public const int notification_tile_bg = 2131165340;
// aapt resource value: 0x7F07009D // aapt resource value: 0x7F07009D
public const int sackdollar = 2131165341; public const int notify_panel_notification_icon_bg = 2131165341;
// aapt resource value: 0x7F07009E // aapt resource value: 0x7F07009E
public const int settings = 2131165342; public const int online = 2131165342;
// aapt resource value: 0x7F07009F // aapt resource value: 0x7F07009F
public const int splash_logo = 2131165343; public const int pencil = 2131165343;
// aapt resource value: 0x7F0700A0 // aapt resource value: 0x7F0700A0
public const int splash_screen = 2131165344; public const int plus = 2131165344;
// aapt resource value: 0x7F0700A1 // aapt resource value: 0x7F0700A1
public const int test_custom_background = 2131165345; public const int preference_list_divider_material = 2131165345;
// aapt resource value: 0x7F0700A2 // aapt resource value: 0x7F0700A2
public const int tooltip_frame_dark = 2131165346; public const int project = 2131165346;
// aapt resource value: 0x7F0700A3 // aapt resource value: 0x7F0700A3
public const int tooltip_frame_light = 2131165347; public const int rcb = 2131165347;
// aapt resource value: 0x7F0700A4 // aapt resource value: 0x7F0700A4
public const int wallet = 2131165348; public const int right = 2131165348;
// aapt resource value: 0x7F0700A5 // aapt resource value: 0x7F0700A5
public const int xamarin_logo = 2131165349; public const int sackdollar = 2131165349;
// aapt resource value: 0x7F0700A6 // aapt resource value: 0x7F0700A6
public const int yuan = 2131165350; public const int settings = 2131165350;
// aapt resource value: 0x7F0700A7
public const int splash_logo = 2131165351;
// aapt resource value: 0x7F0700A8
public const int splash_screen = 2131165352;
// aapt resource value: 0x7F0700A9
public const int test_custom_background = 2131165353;
// aapt resource value: 0x7F0700AA
public const int tooltip_frame_dark = 2131165354;
// aapt resource value: 0x7F0700AB
public const int tooltip_frame_light = 2131165355;
// aapt resource value: 0x7F0700AC
public const int trans = 2131165356;
// aapt resource value: 0x7F0700AD
public const int wallet = 2131165357;
// aapt resource value: 0x7F0700AE
public const int xamarin_logo = 2131165358;
// aapt resource value: 0x7F0700AF
public const int yuan = 2131165359;
// aapt resource value: 0x7F0700B0
public const int yuebao = 2131165360;
// aapt resource value: 0x7F0700B1
public const int zhaozhaoying = 2131165361;
static Drawable() static Drawable()
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -69,7 +69,10 @@
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" /> <Compile Include="AppDelegate.cs" />
<Compile Include="Renderers\LongPressButtonRenderer.cs" /> <Compile Include="Renderers\LongPressButtonRenderer.cs" />
<Compile Include="Renderers\LongPressGridRenderer.cs" />
<Compile Include="Renderers\OptionDatePickerRenderer.cs" />
<Compile Include="Renderers\OptionEntryRenderer.cs" /> <Compile Include="Renderers\OptionEntryRenderer.cs" />
<Compile Include="Renderers\OptionTimePickerRenderer.cs" />
<Compile Include="Renderers\TintImageRenderer.cs" /> <Compile Include="Renderers\TintImageRenderer.cs" />
<None Include="Entitlements.plist" /> <None Include="Entitlements.plist" />
<None Include="Info.plist" /> <None Include="Info.plist" />
@ -227,6 +230,105 @@
<BundleResource Include="Resources\yuan%402x.png" /> <BundleResource Include="Resources\yuan%402x.png" />
<BundleResource Include="Resources\yuan%403x.png" /> <BundleResource Include="Resources\yuan%403x.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\cmb.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\cmb%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\cmb%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\rcb.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\rcb%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\rcb%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\yuebao.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\yuebao%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\yuebao%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\zhaozhaoying.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\zhaozhaoying%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\zhaozhaoying%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\clothes.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\clothes%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\clothes%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\daily.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\daily%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\daily%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\drink.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\drink%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\drink%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\food.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\food%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\food%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\learn.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\learn%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\learn%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\medical.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\medical%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\medical%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\trans.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\trans%402x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\trans%403x.png" />
</ItemGroup>
<Import Project="..\..\Billing.Shared\Billing.Shared.projitems" Label="Shared" /> <Import Project="..\..\Billing.Shared\Billing.Shared.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -2,6 +2,10 @@
{ {
public static partial class Definition public static partial class Definition
{ {
public static partial (string main, long build) GetVersion() => (
Foundation.NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleShortVersionString").ToString(),
int.Parse(Foundation.NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleVersion").ToString())
);
public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular"; public static partial string GetCascadiaRegularFontFamily() => "CascadiaCode-Regular";
public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold"; public static partial string GetCascadiaBoldFontFamily() => "CascadiaCode-Bold";
public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular"; public static partial string GetRobotoCondensedRegularFontFamily() => "RobotoCondensed-Regular";

View File

@ -32,10 +32,6 @@
<string>13.0</string> <string>13.0</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>org.tsanie.Billing</string> <string>org.tsanie.Billing</string>
<key>CFBundleShortVersionString</key>
<string>0.22.301</string>
<key>CFBundleVersion</key>
<string>2</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>China</string> <string>China</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
@ -48,5 +44,9 @@
</array> </array>
<key>UIFileSharingEnabled</key> <key>UIFileSharingEnabled</key>
<true/> <true/>
<key>CFBundleVersion</key>
<string>3</string>
<key>CFBundleShortVersionString</key>
<string>0.3.303</string>
</dict> </dict>
</plist> </plist>

View File

@ -15,7 +15,13 @@ namespace Billing.iOS.Renderers
{ {
base.OnElementChanged(e); base.OnElementChanged(e);
if (Element is LongPressButton) if (e.OldElement != null && longGesture != null)
{
RemoveGestureRecognizer(longGesture);
longGesture = null;
}
if (e.NewElement != null)
{ {
longGesture = new UILongPressGestureRecognizer(OnLongPressed); longGesture = new UILongPressGestureRecognizer(OnLongPressed);
AddGestureRecognizer(longGesture); AddGestureRecognizer(longGesture);

View File

@ -0,0 +1,50 @@
using Billing.iOS.Renderers;
using Billing.UI;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(LongPressGrid), typeof(LongPressGridRenderer))]
namespace Billing.iOS.Renderers
{
public class LongPressGridRenderer : ViewRenderer<LongPressGrid, UIView>
{
private UILongPressGestureRecognizer longGesture;
protected override void OnElementChanged(ElementChangedEventArgs<LongPressGrid> e)
{
base.OnElementChanged(e);
if (e.OldElement != null && longGesture != null)
{
RemoveGestureRecognizer(longGesture);
longGesture = null;
}
if (e.NewElement != null)
{
longGesture = new UILongPressGestureRecognizer(OnLongPressed);
AddGestureRecognizer(longGesture);
}
}
protected override void Dispose(bool disposing)
{
if (longGesture != null)
{
RemoveGestureRecognizer(longGesture);
longGesture = null;
}
base.Dispose(disposing);
}
private void OnLongPressed(UILongPressGestureRecognizer e)
{
if (e.State == UIGestureRecognizerState.Began && Element != null)
{
Element.TriggerLongPress();
}
}
}
}

View File

@ -0,0 +1,23 @@
using Billing.iOS.Renderers;
using Billing.UI;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(OptionDatePicker), typeof(OptionDatePickerRenderer))]
namespace Billing.iOS.Renderers
{
public class OptionDatePickerRenderer : DatePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged(e);
var control = Control;
if (control != null)
{
control.BorderStyle = UITextBorderStyle.None;
}
}
}
}

View File

@ -0,0 +1,23 @@
using Billing.iOS.Renderers;
using Billing.UI;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(OptionTimePicker), typeof(OptionTimePickerRenderer))]
namespace Billing.iOS.Renderers
{
public class OptionTimePickerRenderer : TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
var control = Control;
if (control != null)
{
control.BorderStyle = UITextBorderStyle.None;
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

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