Gui-Tuning

This commit is contained in:
2025-01-02 11:59:59 +01:00
parent bfd995e46b
commit 4a6a17f729
9 changed files with 199 additions and 102 deletions

View File

@@ -0,0 +1,21 @@
using System.Globalization;
/// Gib true zurück, wenn die Collection Werte enthält
namespace Jugenddienst_Stunden.Converter {
internal class CollectionVisibilityConverter : IValueConverter {
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
if (value is IEnumerable<object> collection) {
if ((string)parameter == "Invert")
return !collection.Any();
return collection.Any();
}
if ((string)parameter == "Invert")
return true;
return false;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Globalization;
namespace Jugenddienst_Stunden.Converter;
internal class StringVisibilityConverter : IValueConverter {
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
if (value is string strValue) {
return !string.IsNullOrEmpty(strValue.Replace("Server: ",""));
}
return false;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}

View File

@@ -213,12 +213,12 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.91">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.91" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
<PackageReference Include="Microsoft.Maui.Graphics" Version="8.0.91" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="ZXing.Net.Maui.Controls" Version="0.4.0" />

View File

@@ -17,6 +17,7 @@ public static class MauiProgram {
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
//.UseBarcodeScanning();
.UseBarcodeReader();
#if DEBUG

View File

@@ -1,8 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:supportsRtl="true" android:label="Stunden" android:usesCleartextTraffic="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/appicon"
android:supportsRtl="true"
android:label="Stunden"
/>
<!-- android:usesCleartextTraffic="true" -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
</manifest>

View File

@@ -1,45 +1,31 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Jugenddienst_Stunden.ViewModels;
namespace Jugenddienst_Stunden.ViewModels;
/// <summary>
/// Die Loginseite
/// </summary>
public class LoginViewModel {
/// <summary>
/// Name der Anwendung
/// </summary>
public string AppTitle => AppInfo.Name;
/// <summary>
/// Programmversion
/// </summary>
public string Version => AppInfo.VersionString;
/// <summary>
/// Kurze Mitteilung für den Anwender
/// </summary>
public string Message => "Scanne den QR-Code von deinem Benutzerprofil auf der Stundenseite.";
/// <summary>
/// Genutzer Server für die API
/// </summary>
public string Server { get; set; } = "Server: " + Preferences.Default.Get("apiUrl", "").Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
/// <summary>
/// Titel der Seite - im Moment der aktuelle Anwender
/// </summary>
public string Title { get; set; } = Preferences.Default.Get("name", "") + " " + Preferences.Default.Get("surname", "");
//public event EventHandler<string> AlertEvent;
//public event EventHandler<string> InfoEvent;
//public event Func<string,string, Task> MsgEvent;
/// <summary>
/// Name der Anwendung
/// </summary>
public string AppTitle => AppInfo.Name;
/// <summary>
/// Programmversion
/// </summary>
public string Version => AppInfo.VersionString;
/// <summary>
/// Kurze Mitteilung für den Anwender
/// </summary>
public string Message { get; set; } = "Scanne den QR-Code von deinem Benutzerprofil auf der Stundenseite.";
/// <summary>
/// Genutzer Server für die API
/// </summary>
public string Server { get; set; } = "Server: " + Preferences.Default.Get("apiUrl", "").Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
/// <summary>
/// Titel der Seite - im Moment der aktuelle Anwender
/// </summary>
public string Title { get; set; } = Preferences.Default.Get("name", "Nicht") + " " + Preferences.Default.Get("surname", "eingeloggt");
}

View File

@@ -2,52 +2,72 @@
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:Jugenddienst_Stunden.ViewModels"
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls"
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls"
xmlns:conv="clr-namespace:Jugenddienst_Stunden.Converter"
x:Class="Jugenddienst_Stunden.Views.LoginPage"
Title="{Binding Title}">
<ContentPage.BindingContext>
<models:LoginViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<conv:StringVisibilityConverter x:Key="StringVisibilityConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ScrollView>
<VerticalStackLayout Spacing="10" Margin="15">
<HorizontalStackLayout Spacing="10">
<Label FontSize="22" FontAttributes="Bold" Text="{Binding AppTitle}" VerticalOptions="End" />
<Label FontSize="22" Text="{Binding Version}" VerticalOptions="End" />
<Label FontSize="22" FontAttributes="Bold" Text="{Binding AppTitle}" Margin="0,4,0,0" />
<Label FontSize="22" Text="{Binding Version}" Margin="0,4,0,0" />
<HorizontalStackLayout HorizontalOptions="End" Spacing="10" Margin="25,0,0,0">
<Label Text="Login QR/manuell" Margin="0,12,0,0"/>
<Switch x:Name="LoginSwitch" IsToggled="False" Toggled="Switch_Toggled">
</Switch>
</HorizontalStackLayout>
</HorizontalStackLayout>
<Label Text="{Binding Message}" HeightRequest="40" />
<Label x:Name="ServerLabel" Text="{Binding Server}" HeightRequest="25" Margin="0,0,0,10" />
<Frame HeightRequest="300" Padding="0" CornerRadius="0">
<Label x:Name="Message" Text="{Binding Message}" HeightRequest="40" />
<Label x:Name="ServerLabel" Text="{Binding Server}" HeightRequest="25" Margin="0,0,0,10" IsVisible="{Binding Server, Converter={StaticResource StringVisibilityConverter}}" />
<Frame x:Name="LoginQR" HeightRequest="300" Padding="0" CornerRadius="0">
<zxing:CameraBarcodeReaderView x:Name="barcodeScannerView"
BarcodesDetected="BarcodesDetected"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"/>
</Frame>
<VerticalStackLayout Padding="30" Spacing="15">
<VerticalStackLayout x:Name="LoginManual" Padding="30" Spacing="15">
<Label Text="Manueller Login"
FontSize="32"
HorizontalOptions="Start" />
FontSize="32"
HorizontalOptions="Start" />
<Entry x:Name="UsernameEntry"
Placeholder="Benutzername"
Keyboard="Email" />
Placeholder="Benutzername"
Keyboard="Email" />
<Entry x:Name="PasswordEntry"
Placeholder="Passwort"
IsPassword="True" />
Placeholder="Passwort"
IsPassword="True" />
<Entry x:Name="ServerEntry"
Placeholder="Server"
Keyboard="Url" />
Placeholder="Server"
Keyboard="Url" />
<Button Text="Login" Clicked="OnLoginButtonClicked" />
</VerticalStackLayout>
</VerticalStackLayout>
</ScrollView>

View File

@@ -1,6 +1,7 @@
using Jugenddienst_Stunden.Models;
using ZXing.Net.Maui;
using Jugenddienst_Stunden.Types;
using ZXing.Net.Maui;
namespace Jugenddienst_Stunden.Views;
@@ -13,14 +14,12 @@ public partial class LoginPage : ContentPage {
private readonly TimeSpan _detectionInterval = TimeSpan.FromSeconds(5);
/// <summary>
/// CTOR
/// </summary>
public LoginPage() {
InitializeComponent();
barcodeScannerView.Options = new BarcodeReaderOptions {
Formats = BarcodeFormat.QrCode,
AutoRotate = true,
@@ -32,10 +31,28 @@ public partial class LoginPage : ContentPage {
// vm.InfoEvent += Vm_InfoEvent;
// vm.MsgEvent += Vm_MsgEvent;
//}
//Zwischen manuellem und automatischem Login (mit QR-Code) umschalten und die Schalterstellung merken
bool sqr = true;
bool sma = false;
if (Preferences.Default.Get("logintype", "") == "manual") {
sqr = false;
sma = true;
LoginSwitch.IsToggled = true;
Message.IsVisible = false;
} else {
LoginSwitch.IsToggled = false;
Message.IsVisible = true;
}
LoginQR.IsVisible = sqr;
LoginManual.IsVisible = sma;
}
/// <summary>
/// Nach der Erkennung des Barcodes wird der Benutzer eingeloggt
/// ZXing.Net.Maui.Controls 0.4.4
/// </summary>
private void BarcodesDetected(object sender, BarcodeDetectionEventArgs e) {
var currentTime = DateTime.Now;
@@ -57,12 +74,9 @@ public partial class LoginPage : ContentPage {
GlobalVar.EmployeeId = user.Id;
Title = user.Name + " " + user.Surname;
//Auf der Loginseite wird der Server als Info ohne Protokoll und ohne /appapi angezeigt
ServerLabel.Text = "Server: " + tokendata.Url.Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
//Preferences.Default.Set("apiKey", barcode.Value);
//Preferences.Default.Set("name", op.name);
//Preferences.Default.Set("surname", op.surname);
//Preferences.Default.Set("EmployeeId", int.Parse(op.id));
await DisplayAlert("Login erfolgreich", user.Name + " " + user.Surname, "OK");
if (Navigation.NavigationStack.Count > 1)
@@ -90,7 +104,6 @@ public partial class LoginPage : ContentPage {
barcodeScannerView.CameraLocation = CameraLocation.Front;
barcodeScannerView.IsDetecting = false;
}
protected override void OnAppearing() {
@@ -98,7 +111,6 @@ public partial class LoginPage : ContentPage {
barcodeScannerView.IsDetecting = true;
barcodeScannerView.CameraLocation = CameraLocation.Rear;
}
public bool IsCameraAvailable() {
@@ -138,6 +150,24 @@ public partial class LoginPage : ContentPage {
}
}
//Zwischen manuellem und automatischem Login (mit QR-Code) umschalten und die Schalterstellung merken
private void Switch_Toggled(object sender, ToggledEventArgs e) {
var switcher = (Switch)sender;
if (switcher.IsToggled) {
LoginQR.IsVisible = false;
LoginManual.IsVisible = true;
Message.IsVisible = false;
Preferences.Default.Set("logintype", "manual");
} else {
LoginQR.IsVisible = true;
LoginManual.IsVisible = false;
Message.IsVisible = true;
Preferences.Default.Set("logintype", "qr");
}
}
//private void Vm_AlertEvent(object? sender, string e) {
// DisplayAlert("Fehler:", e, "OK");
//}

View File

@@ -13,12 +13,15 @@
<ContentPage.Resources>
<ResourceDictionary>
<conv:IntBoolConverter x:Key="IntBoolConverter" />
<conv:CollectionVisibilityConverter x:Key="CollectionVisibilityConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<VerticalStackLayout Spacing="10" Margin="10">
<Label Text="{Binding SubTitle}" />
<Label Text="{Binding SubTitle}" FontSize="Medium" Margin="4,0,0,0" />
<Frame Padding="5,2,5,10">
<FlexLayout Direction="Row" AlignItems="Start" Wrap="Wrap" JustifyContent="SpaceBetween">
@@ -36,7 +39,7 @@
</FlexLayout>
</Frame>
<Frame Padding="5,2,5,10">
<Frame Padding="5,4,5,8">
<Grid ColumnDefinitions="*,*,*">
<Picker x:Name="pick_gemeinde" Title="Gemeinde" ItemsSource="{Binding OptionsGemeinde}" SelectedItem="{Binding DayTime.GemeindeAktiv, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="0" IsVisible="{Binding GemeindeAktivSet}">
</Picker>
@@ -54,10 +57,25 @@
<Button Grid.Column="1" Text="Löschen" Command="{Binding DeleteConfirmCommand}" IsEnabled="{Binding DayTime.Id, Converter={StaticResource IntBoolConverter}}" IsVisible="{Binding FreistellungEnabled}" />
</Grid>
<BoxView HeightRequest="1" Margin="5,10"/>
<ScrollView>
<CollectionView
<BoxView HeightRequest="1" Margin="3,10" />
<Label Text="Noch keine Einträge vorhanden" IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}, ConverterParameter=Invert}" Margin="6,0,0,0"/>
<StackLayout Margin="6,0,0,0" IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}}">
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="Am " />
<Span Text="{Binding SubTitle}" />
<Span Text=" vorhandene Einträge:"/>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
<ScrollView IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}}">
<CollectionView
ItemsSource="{Binding DayTimes}"
x:Name="stundeItems" Margin="0"
HeightRequest="350"
@@ -65,36 +83,36 @@
SelectionChangedCommand="{Binding SelectEntryCommand}"
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="0" />
</CollectionView.ItemsLayout>
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="0" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="5,10,5,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<HorizontalStackLayout Grid.Row="0" Grid.Column="0">
<Label Grid.Column="0" Text="{Binding Begin}"/>
<Label Text="bis" Padding="5,0,5,0"/>
<Label Text="{Binding End}"/>
<Label Text="{Binding GemeindeAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.GemeindeAktivSet}"/>
<Label Text="{Binding ProjektAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ProjektAktivSet}"/>
<Label Text="{Binding FreistellungAktiv.Name}" Margin="10,0,0,0"/>
</HorizontalStackLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="5,10,5,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<HorizontalStackLayout Grid.Row="0" Grid.Column="0">
<Label Grid.Column="0" Text="{Binding Begin}"/>
<Label Text="bis" Padding="5,0,5,0"/>
<Label Text="{Binding End}"/>
<Label Text="{Binding GemeindeAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.GemeindeAktivSet}"/>
<Label Text="{Binding ProjektAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ProjektAktivSet}"/>
<Label Text="{Binding FreistellungAktiv.Name}" Margin="10,0,0,0"/>
</HorizontalStackLayout>
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Description}" Padding="0,0,0,15"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Description}" Padding="0,0,0,15"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</CollectionView>
</ScrollView>
</VerticalStackLayout>
</ContentPage>