This commit is contained in:
David Britch 2023-11-23 15:48:07 +00:00 коммит произвёл GitHub
Родитель af6124c34a
Коммит 38f5f7f50a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
163 изменённых файлов: 8022 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34219.65
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformIntegrationDemo", "PlatformIntegrationDemo\PlatformIntegrationDemo.csproj", "{1420EC25-7D89-4DB0-9D6B-D9A99F0D892E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1420EC25-7D89-4DB0-9D6B-D9A99F0D892E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1420EC25-7D89-4DB0-9D6B-D9A99F0D892E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1420EC25-7D89-4DB0-9D6B-D9A99F0D892E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{1420EC25-7D89-4DB0-9D6B-D9A99F0D892E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1420EC25-7D89-4DB0-9D6B-D9A99F0D892E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E78EE56A-FD76-40CF-9335-6C4746F9AA6A}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -0,0 +1,28 @@
<?xml version = "1.0" encoding = "UTF-8" ?>
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:PlatformIntegrationDemo.Converters"
x:Class="PlatformIntegrationDemo.App">
<Application.Resources>
<ResourceDictionary>
<converters:InverterConverter x:Key="InverterConverter" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="StackLayout">
<Setter Property="Spacing"
Value="6" />
</Style>
<Style TargetType="Grid">
<Setter Property="RowSpacing"
Value="6" />
<Setter Property="ColumnSpacing"
Value="6" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

Просмотреть файл

@ -0,0 +1,33 @@
using PlatformIntegrationDemo.Views;
namespace PlatformIntegrationDemo;
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
public static void HandleAppActions(AppAction appAction)
{
App.Current.Dispatcher.Dispatch(async () =>
{
var page = appAction.Id switch
{
"battery_info" => new BatteryPage(),
"app_info" => new AppInfoPage(),
_ => default(Page)
};
if (page != null)
{
await Application.Current.MainPage.Navigation.PopToRootAsync();
await Application.Current.MainPage.Navigation.PushAsync(page);
}
});
}
}

Просмотреть файл

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="PlatformIntegrationDemo.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
Shell.FlyoutBehavior="Disabled"
Title="PlatformIntegrationDemo">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate views:MainPage}"
Route="MainPage" />
</Shell>

Просмотреть файл

@ -0,0 +1,10 @@
namespace PlatformIntegrationDemo;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,24 @@
using System.Globalization;
namespace PlatformIntegrationDemo.Converters
{
public class InverterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool v)
return !v;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool v)
return !v;
else
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
namespace PlatformIntegrationDemo.Helpers
{
public static class ViewHelpers
{
public static Rect GetAbsoluteBounds(this Microsoft.Maui.Controls.View element)
{
Element looper = element;
var absoluteX = element.X + element.Margin.Top;
var absoluteY = element.Y + element.Margin.Left;
while (looper.Parent != null)
{
looper = looper.Parent;
if (looper is Microsoft.Maui.Controls.View v)
{
absoluteX += v.X + v.Margin.Top;
absoluteY += v.Y + v.Margin.Left;
}
}
return new Rect(absoluteX, absoluteY, element.Width, element.Height);
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
namespace PlatformIntegrationDemo;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.ConfigureEssentials(essentials =>
{
essentials.UseVersionTracking();
essentials.AddAppAction("app_info", "App Info", icon: "app_info_action_icon");
essentials.AddAppAction("battery_info", "Battery Info");
essentials.OnAppAction(App.HandleAppActions);
#if WINDOWS
essentials.UseMapServiceToken("INSERT_MAP_TOKEN_HERE");
#endif
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}

Просмотреть файл

@ -0,0 +1,67 @@
using System.Windows.Input;
using CommunityToolkit.Mvvm.Messaging;
using PlatformIntegrationDemo.ViewModels;
namespace PlatformIntegrationDemo.Models
{
public class PermissionItem : ObservableObject
{
public PermissionItem(string title, Permissions.BasePermission permission)
{
Title = title;
Permission = permission;
Status = PermissionStatus.Unknown;
}
public string Title { get; set; }
public string Rationale { get; set; }
public PermissionStatus Status { get; set; }
public Permissions.BasePermission Permission { get; set; }
public ICommand CheckStatusCommand =>
new Command(async () =>
{
try
{
Status = await Permission.CheckStatusAsync();
OnPropertyChanged(nameof(Status));
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(ex, nameof(PermissionException));
}
});
public ICommand RequestCommand =>
new Command(async () =>
{
try
{
Status = await Permission.RequestAsync();
OnPropertyChanged(nameof(Status));
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(ex, nameof(PermissionException));
}
});
public ICommand ShouldShowRationaleCommand =>
new Command(() =>
{
try
{
Rationale = $"Should show rationale: {Permission.ShouldShowRationale()}";
OnPropertyChanged(nameof(Rationale));
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(ex, nameof(PermissionException));
}
});
}
}

Просмотреть файл

@ -0,0 +1,21 @@
namespace PlatformIntegrationDemo.Models
{
public class SampleItem
{
public string Icon { get; }
public string Name { get; }
public string Description { get; }
public Type PageType { get; }
public string[] Tags { get; }
public SampleItem(string icon, string name, Type pageType, string description, params string[] tags)
{
Icon = icon;
Name = name;
Description = description;
PageType = pageType;
Tags = tags ?? new string[0];
}
}
}

Просмотреть файл

@ -0,0 +1,67 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifer>.
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
<OutputType>Exe</OutputType>
<RootNamespace>PlatformIntegrationDemo</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Display name -->
<ApplicationTitle>PlatformIntegrationDemo</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.companyname.platformintegrationdemo</ApplicationId>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.3" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\Images\app_info_action_icon.png" />
<BundleResource Include="Resources\Images\battery_action_icon.png" />
<BundleResource Include="Resources\Raw\FileSystemTemplate.txt" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,54 @@
<?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:roundIcon="@mipmap/appicon_round" android:supportsRtl="true" android:fullBackupContent="@xml/my_backup_rules"></application>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<queries>
<!-- Email -->
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<!-- Sms -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="smsto" />
</intent>
<!-- PhoneDialer -->
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<!-- MediaPicker -->
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
<!-- Maps -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
</intent>
</queries>
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
</manifest>

Просмотреть файл

@ -0,0 +1,62 @@
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Widget;
namespace PlatformIntegrationDemo;
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density, ScreenOrientation = ScreenOrientation.FullSensor)]
[IntentFilter(
new[] { Microsoft.Maui.ApplicationModel.Platform.Intent.ActionAppAction },
Categories = new[] { Intent.CategoryDefault })]
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Microsoft.Maui.ApplicationModel.Platform.Init(this, bundle);
Microsoft.Maui.ApplicationModel.Platform.ActivityStateChanged += Platform_ActivityStateChanged;
}
protected override void OnResume()
{
base.OnResume();
Microsoft.Maui.ApplicationModel.Platform.OnResume(this);
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
Microsoft.Maui.ApplicationModel.Platform.OnNewIntent(intent);
}
protected override void OnDestroy()
{
base.OnDestroy();
Microsoft.Maui.ApplicationModel.Platform.ActivityStateChanged -= Platform_ActivityStateChanged;
}
void Platform_ActivityStateChanged(object sender, Microsoft.Maui.ApplicationModel.ActivityStateChangedEventArgs e) =>
Toast.MakeText(this, e.State.ToString(), ToastLength.Short).Show();
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Microsoft.Maui.ApplicationModel.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(
new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = "xamarinessentials")]
public class WebAuthenticationCallbackActivity : Microsoft.Maui.Authentication.WebAuthenticatorCallbackActivity
{
}

Просмотреть файл

@ -0,0 +1,16 @@
using Android.App;
using Android.Runtime;
namespace PlatformIntegrationDemo;
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>

Просмотреть файл

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="${applicationId}.xamarinessentials.xml"/>
</full-backup-content>

Просмотреть файл

@ -0,0 +1,10 @@
using Foundation;
namespace PlatformIntegrationDemo;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- The Mac App Store requires you specify if the app uses encryption. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
<!-- Please indicate <true/> or <false/> here. -->
<!-- Specify the category for your app here. -->
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
<!-- <key>LSApplicationCategoryType</key> -->
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
<key>UIDeviceFamily</key>
<array>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Access to your location is required for cool things to happen!</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>xamarinessentials</string>
<key>CFBundleURLSchemes</key>
<array>
<string>xamarinessentials</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
<key>NSCameraUsageDescription</key>
<string>Camera Photos</string>
<key>NSCalendarsUsageDescription</key>
<string>Calendar Access</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Get Location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Get Location</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Pick Photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Pick Photos</string>
<key>NSMicrophoneUsageDescription</key>
<string>Catpure Video</string>
<key>NSContactsUsageDescription</key>
<string>Contacts</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mailto</string>
</array>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,16 @@
using ObjCRuntime;
using UIKit;
namespace PlatformIntegrationDemo;
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}

Просмотреть файл

@ -0,0 +1,17 @@
using System;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
namespace PlatformIntegrationDemo;
class Program : MauiApplication
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
static void Main(string[] args)
{
var app = new Program();
app.Run(args);
}
}

Просмотреть файл

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="7" xmlns="http://tizen.org/ns/packages">
<profile name="common" />
<ui-application appid="maui-application-id-placeholder" exec="PlatformIntegrationDemo.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
<label>maui-application-title-placeholder</label>
<icon>maui-appicon-placeholder</icon>
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
</ui-application>
<shortcut-list />
<privileges>
<privilege>http://tizen.org/privilege/internet</privilege>
</privileges>
<dependencies />
<provides-appdefined-privileges />
</manifest>

Просмотреть файл

@ -0,0 +1,8 @@
<maui:MauiWinUIApplication
x:Class="PlatformIntegrationDemo.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:maui="using:Microsoft.Maui"
xmlns:local="using:PlatformIntegrationDemo.WinUI">
</maui:MauiWinUIApplication>

Просмотреть файл

@ -0,0 +1,25 @@
using Microsoft.UI.Xaml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace PlatformIntegrationDemo.WinUI;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : MauiWinUIApplication
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
<Identity Name="maui-package-name-placeholder" Publisher="CN=User Name" Version="0.0.0.0" />
<mp:PhoneIdentity PhoneProductId="2C7A2684-B1EA-4BBF-8818-A199BFB9D3AA" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>$placeholder$</DisplayName>
<PublisherDisplayName>User Name</PublisherDisplayName>
<Logo>$placeholder$.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
<uap:VisualElements
DisplayName="$placeholder$"
Description="$placeholder$"
Square150x150Logo="$placeholder$.png"
Square44x44Logo="$placeholder$.png"
BackgroundColor="transparent">
<uap:DefaultTile Square71x71Logo="$placeholder$.png" Wide310x150Logo="$placeholder$.png" Square310x310Logo="$placeholder$.png" />
<uap:SplashScreen Image="$placeholder$.png" />
</uap:VisualElements>
<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="xamarinessentials">
<uap:DisplayName>Xamarin Essentials</uap:DisplayName>
</uap:Protocol>
</uap:Extension>
</Extensions>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="runFullTrust" />
</Capabilities>
</Package>

Просмотреть файл

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="PlatformIntegrationDemo.WinUI.app"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

Просмотреть файл

@ -0,0 +1,10 @@
using Foundation;
namespace PlatformIntegrationDemo;
[Register("AppDelegate")]
public class AppDelegate : MauiUIApplicationDelegate
{
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}

Просмотреть файл

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
</array>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Access to your location is required for cool things to happen!</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>xamarinessentials</string>
<key>CFBundleURLSchemes</key>
<array>
<string>xamarinessentials</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
</dict>
</array>
<key>NSCameraUsageDescription</key>
<string>Camera Photos</string>
<key>NSCalendarsUsageDescription</key>
<string>Calendar Access</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Get Location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Get Location</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Add Photos</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Pick Photos</string>
<key>NSMicrophoneUsageDescription</key>
<string>Catpure Video</string>
<key>NSContactsUsageDescription</key>
<string>Contacts</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mailto</string>
</array>
</dict>
</plist>

Просмотреть файл

@ -0,0 +1,16 @@
using ObjCRuntime;
using UIKit;
namespace PlatformIntegrationDemo;
public class Program
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));
}
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"profiles": {
"Windows Machine": {
"commandName": "MsixPackage",
"nativeDebugging": false
}
}
}

Просмотреть файл

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
</svg>

Просмотреть файл

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.7 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.9 KiB

Просмотреть файл

@ -0,0 +1,18 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories). Deployment of the asset to your application
is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
These files will be deployed with you package and will be accessible using Essentials:
async Task LoadMauiAsset()
{
using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
}

Просмотреть файл

@ -0,0 +1,4 @@
This file was loaded from the app package.
You can use this as a starting point for your comments...

Просмотреть файл

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
</svg>

Просмотреть файл

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<!-- Note: For Android please see also Platforms\Android\Resources\values\colors.xml -->
<Color x:Key="Primary">#512BD4</Color>
<Color x:Key="Secondary">#DFD8F7</Color>
<Color x:Key="Tertiary">#2B0B98</Color>
<Color x:Key="White">White</Color>
<Color x:Key="Black">Black</Color>
<Color x:Key="Gray100">#E1E1E1</Color>
<Color x:Key="Gray200">#C8C8C8</Color>
<Color x:Key="Gray300">#ACACAC</Color>
<Color x:Key="Gray400">#919191</Color>
<Color x:Key="Gray500">#6E6E6E</Color>
<Color x:Key="Gray600">#404040</Color>
<Color x:Key="Gray900">#212121</Color>
<Color x:Key="Gray950">#141414</Color>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}"/>
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}"/>
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}"/>
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}"/>
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}"/>
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}"/>
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}"/>
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}"/>
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}"/>
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}"/>
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}"/>
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}"/>
<Color x:Key="Yellow100Accent">#F7B548</Color>
<Color x:Key="Yellow200Accent">#FFD590</Color>
<Color x:Key="Yellow300Accent">#FFE5B9</Color>
<Color x:Key="Cyan100Accent">#28C2D1</Color>
<Color x:Key="Cyan200Accent">#7BDDEF</Color>
<Color x:Key="Cyan300Accent">#C3F2F4</Color>
<Color x:Key="Blue100Accent">#3E8EED</Color>
<Color x:Key="Blue200Accent">#72ACF1</Color>
<Color x:Key="Blue300Accent">#A7CBF6</Color>
</ResourceDictionary>

Просмотреть файл

@ -0,0 +1,408 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Style TargetType="ActivityIndicator">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="IndicatorView">
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"/>
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}"/>
</Style>
<Style TargetType="Border">
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="StrokeShape" Value="Rectangle"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="BoxView">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Button">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Primary}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14,10"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="DatePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Editor">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Entry">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Frame">
<Setter Property="HasShadow" Value="False" />
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style TargetType="ImageButton">
<Setter Property="Opacity" Value="1" />
<Setter Property="BorderColor" Value="Transparent"/>
<Setter Property="BorderWidth" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="Opacity" Value="0.5" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="PointerOver" />
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Label">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ListView">
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="Picker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RadioButton">
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="RefreshView">
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="SearchBar">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SearchHandler">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="FontFamily" Value="OpenSansRegular" />
<Setter Property="FontSize" Value="14" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Shadow">
<Setter Property="Radius" Value="15" />
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Brush" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Offset" Value="10,10" />
</Style>
<Style TargetType="Slider">
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="SwipeItem">
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Switch">
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="ThumbColor" Value="{StaticResource White}" />
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="TimePicker">
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
<Setter Property="BackgroundColor" Value="Transparent"/>
<Setter Property="FontFamily" Value="OpenSansRegular"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinimumHeightRequest" Value="44"/>
<Setter Property="MinimumWidthRequest" Value="44"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<VisualState.Setters>
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Padding" Value="0"/>
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
</Style>
<Style TargetType="Shell" ApplyToDerivedTypes="True">
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.ForegroundColor" Value="{OnPlatform WinUI={StaticResource Primary}, Default={StaticResource White}}" />
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}" />
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
<Setter Property="Shell.NavBarHasShadow" Value="False" />
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
</Style>
<Style TargetType="TabbedPage">
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
</Style>
</ResourceDictionary>

Просмотреть файл

@ -0,0 +1,134 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class AccelerometerViewModel : BaseViewModel
{
double x;
double y;
double z;
string shakeTime = string.Empty;
bool isActive;
int speed = 0;
public AccelerometerViewModel()
{
StartCommand = new Command(OnStart);
StopCommand = new Command(OnStop);
}
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public string ShakeTime
{
get => shakeTime;
set => SetProperty(ref shakeTime, value);
}
public double X
{
get => x;
set => SetProperty(ref x, value);
}
public double Y
{
get => y;
set => SetProperty(ref y, value);
}
public double Z
{
get => z;
set => SetProperty(ref z, value);
}
public bool IsActive
{
get => isActive;
set => SetProperty(ref isActive, value);
}
public string[] Speeds { get; } =
Enum.GetNames(typeof(SensorSpeed));
public int Speed
{
get => speed;
set => SetProperty(ref speed, value);
}
public override void OnAppearing()
{
Accelerometer.ReadingChanged += OnReadingChanged;
Accelerometer.ShakeDetected += Accelerometer_OnShaked;
base.OnAppearing();
}
void Accelerometer_OnShaked(object sender, EventArgs e) =>
ShakeTime = $"Shake detected: {DateTime.Now.ToLongTimeString()}";
public override void OnDisappearing()
{
OnStop();
Accelerometer.ReadingChanged -= OnReadingChanged;
Accelerometer.ShakeDetected -= Accelerometer_OnShaked;
base.OnDisappearing();
}
async void OnStart()
{
try
{
Accelerometer.Start((SensorSpeed)Speed);
IsActive = true;
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to start accelerometer: {ex.Message}");
}
}
void OnStop()
{
try
{
Accelerometer.Stop();
IsActive = false;
}
catch (Exception ex)
{
Debug.WriteLine("Unable to stop accelerometer: {0}", ex);
}
}
void OnReadingChanged(object sender, AccelerometerChangedEventArgs e)
{
var data = e.Reading;
switch ((SensorSpeed)Speed)
{
case SensorSpeed.Fastest:
case SensorSpeed.Game:
MainThread.BeginInvokeOnMainThread(() =>
{
X = data.Acceleration.X;
Y = data.Acceleration.Y;
Z = data.Acceleration.Z;
});
break;
default:
X = data.Acceleration.X;
Y = data.Acceleration.Y;
Z = data.Acceleration.Z;
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class AppInfoViewModel : BaseViewModel
{
public string AppPackageName => AppInfo.PackageName;
public string AppName => AppInfo.Name;
public string AppVersion => AppInfo.VersionString;
public string AppBuild => AppInfo.BuildString;
public string AppTheme => AppInfo.RequestedTheme.ToString();
public Command ShowSettingsUICommand { get; }
public AppInfoViewModel()
{
ShowSettingsUICommand = new Command(() => AppInfo.ShowSettingsUI());
}
}
}

Просмотреть файл

@ -0,0 +1,90 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class BarometerViewModel : BaseViewModel
{
bool isActive;
double pressure;
int speed = 0;
public BarometerViewModel()
{
StartCommand = new Command(OnStartBarometer);
StopCommand = new Command(OnStop);
}
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public bool IsActive
{
get => isActive;
set => SetProperty(ref isActive, value);
}
public double Pressure
{
get => pressure;
set => SetProperty(ref pressure, value);
}
public string[] Speeds { get; } = Enum.GetNames(typeof(SensorSpeed));
public int Speed
{
get => speed;
set => SetProperty(ref speed, value);
}
public override void OnAppearing()
{
Barometer.ReadingChanged += OnBarometerReadingChanged;
base.OnAppearing();
}
public override void OnDisappearing()
{
OnStop();
Barometer.ReadingChanged -= OnBarometerReadingChanged;
base.OnDisappearing();
}
async void OnStartBarometer()
{
try
{
Barometer.Start((SensorSpeed)Speed);
IsActive = true;
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to start barometer: {ex.Message}");
}
}
void OnStop()
{
try
{
Barometer.Stop();
IsActive = false;
}
catch (Exception ex)
{
Debug.WriteLine("Unable to stop barometer: {0}", ex);
}
}
void OnBarometerReadingChanged(object sender, BarometerChangedEventArgs e)
{
Pressure = e.Reading.PressureInHectopascals;
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class BaseViewModel : ObservableObject
{
bool isBusy;
public bool IsBusy
{
get => isBusy;
set => SetProperty(ref isBusy, value, onChanged: () => OnPropertyChanged(nameof(IsNotBusy)));
}
public bool IsNotBusy => !IsBusy;
public virtual void OnAppearing()
{
}
public virtual void OnDisappearing()
{
}
internal event Func<string, Task> DoDisplayAlert;
internal event Func<BaseViewModel, bool, Task> DoNavigate;
public Task DisplayAlertAsync(string message)
{
return DoDisplayAlert?.Invoke(message) ?? Task.CompletedTask;
}
public Task NavigateAsync(BaseViewModel vm, bool showModal = false)
{
return DoNavigate?.Invoke(vm, showModal) ?? Task.CompletedTask;
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class BatteryViewModel : BaseViewModel
{
public BatteryViewModel()
{
}
public double Level => Battery.ChargeLevel;
public BatteryState State => Battery.State;
public BatteryPowerSource PowerSource => Battery.PowerSource;
public EnergySaverStatus EnergySaverStatus => Battery.EnergySaverStatus;
public override void OnAppearing()
{
base.OnAppearing();
Battery.BatteryInfoChanged += OnBatteryInfoChanged;
Battery.EnergySaverStatusChanged += OnEnergySaverStatusChanged;
}
public override void OnDisappearing()
{
Battery.BatteryInfoChanged -= OnBatteryInfoChanged;
Battery.EnergySaverStatusChanged -= OnEnergySaverStatusChanged;
base.OnDisappearing();
}
void OnEnergySaverStatusChanged(object sender, EnergySaverStatusChangedEventArgs e)
{
OnPropertyChanged(nameof(EnergySaverStatus));
}
void OnBatteryInfoChanged(object sender, BatteryInfoChangedEventArgs e)
{
OnPropertyChanged(nameof(Level));
OnPropertyChanged(nameof(State));
OnPropertyChanged(nameof(PowerSource));
}
}
}

Просмотреть файл

@ -0,0 +1,152 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class BrowserViewModel : BaseViewModel
{
string browserStatus;
string uri = "https://learn.microsoft.com/dotnet/maui";
int browserType = (int)BrowserLaunchMode.SystemPreferred;
int browserTitleType = (int)BrowserTitleMode.Default;
int controlColor = 0;
int toolbarColor = 0;
bool presentAsFormSheet = false;
bool presentAsPageSheet = false;
bool launchAdjacent = false;
Dictionary<string, Color> colorDictionary;
public List<string> AllColors { get; }
public BrowserViewModel()
{
OpenUriCommand = new Command(OpenUri);
colorDictionary = typeof(Color)
.GetFields()
.Where(f => f.FieldType == typeof(Color) && f.IsStatic && f.IsPublic)
.ToDictionary(f => f.Name, f => (Color)f.GetValue(null));
var colors = colorDictionary.Keys.ToList();
colors.Insert(0, "None");
AllColors = colors;
}
public ICommand OpenUriCommand { get; }
public string BrowserStatus
{
get => browserStatus;
set => SetProperty(ref browserStatus, value);
}
public string Uri
{
get => uri;
set => SetProperty(ref uri, value);
}
public List<string> BrowserLaunchModes { get; } =
new List<string>
{
$"Use System-Preferred Browser",
$"Use Default Browser App"
};
public int BrowserType
{
get => browserType;
set => SetProperty(ref browserType, value);
}
public List<string> BrowserTitleModes { get; } =
new List<string>
{
$"Use Default Mode",
$"Show Title",
$"Hide Title"
};
public int BrowserTitleType
{
get => browserTitleType;
set => SetProperty(ref browserTitleType, value);
}
public int ToolbarColor
{
get => toolbarColor;
set => SetProperty(ref toolbarColor, value);
}
public int ControlColor
{
get => controlColor;
set => SetProperty(ref controlColor, value);
}
public bool PresentAsFormSheet
{
get => presentAsFormSheet;
set => SetProperty(ref presentAsFormSheet, value);
}
public bool PresentAsPageSheet
{
get => presentAsPageSheet;
set => SetProperty(ref presentAsPageSheet, value);
}
public bool LaunchAdjacent
{
get => launchAdjacent;
set => SetProperty(ref launchAdjacent, value);
}
async void OpenUri()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var flags = BrowserLaunchFlags.None;
if (PresentAsPageSheet)
flags |= BrowserLaunchFlags.PresentAsPageSheet;
if (PresentAsFormSheet)
flags |= BrowserLaunchFlags.PresentAsFormSheet;
if (LaunchAdjacent)
flags |= BrowserLaunchFlags.LaunchAdjacent;
await Browser.OpenAsync(uri, new BrowserLaunchOptions
{
LaunchMode = (BrowserLaunchMode)BrowserType,
TitleMode = (BrowserTitleMode)BrowserTitleType,
PreferredToolbarColor = GetColor(ToolbarColor),
PreferredControlColor = GetColor(ControlColor),
Flags = flags
});
}
catch (Exception e)
{
BrowserStatus = $"Unable to open Uri {e.Message}";
Debug.WriteLine(browserStatus);
}
finally
{
IsBusy = false;
}
Color GetColor(int index)
{
return index <= 0
? (Color)null
: colorDictionary[AllColors[index]];
}
}
}
}

Просмотреть файл

@ -0,0 +1,82 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
class ClipboardViewModel : BaseViewModel
{
string fieldValue;
string lastCopied;
public ClipboardViewModel()
{
CopyCommand = new Command(OnCopy);
PasteCommand = new Command(OnPaste);
CheckCommand = new Command(OnCheck);
}
public ICommand CopyCommand { get; }
public ICommand PasteCommand { get; }
public ICommand CheckCommand { get; }
public string FieldValue
{
get => fieldValue;
set => SetProperty(ref fieldValue, value);
}
public string LastCopied
{
get => lastCopied;
set => SetProperty(ref lastCopied, value);
}
public override void OnAppearing()
{
try
{
Clipboard.ClipboardContentChanged += OnClipboardContentChanged;
}
catch (FeatureNotSupportedException)
{
}
}
public override void OnDisappearing()
{
try
{
Clipboard.ClipboardContentChanged -= OnClipboardContentChanged;
}
catch (FeatureNotSupportedException)
{
}
}
void OnClipboardContentChanged(object sender, EventArgs args)
{
LastCopied = $"Last copied text at {DateTime.UtcNow:T}";
}
async void OnCopy()
{
await Clipboard.SetTextAsync(FieldValue);
}
async void OnPaste()
{
var text = await Clipboard.GetTextAsync();
if (!string.IsNullOrWhiteSpace(text))
{
FieldValue = text;
}
}
async void OnCheck()
{
await DisplayAlertAsync($"Has text: {Clipboard.HasText}");
}
}
}

Просмотреть файл

@ -0,0 +1,88 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class ColorConvertersViewModel : BaseViewModel
{
int alpha = 100;
int saturation = 100;
int hue = 360;
int luminosity = 100;
string hex = "#3498db";
public ColorConvertersViewModel()
{
}
public int Alpha
{
get => alpha;
set => SetProperty(ref alpha, value, onChanged: SetColor);
}
public int Luminosity
{
get => luminosity;
set => SetProperty(ref luminosity, value, onChanged: SetColor);
}
public int Hue
{
get => hue;
set => SetProperty(ref hue, value, onChanged: SetColor);
}
public int Saturation
{
get => saturation;
set => SetProperty(ref saturation, value, onChanged: SetColor);
}
public string Hex
{
get => hex;
set => SetProperty(ref hex, value, onChanged: SetColor);
}
public Color RegularColor { get; set; }
public Color AlphaColor { get; set; }
public Color SaturationColor { get; set; }
public Color HueColor { get; set; }
public Color ComplementColor { get; set; }
public Color LuminosityColor { get; set; }
public string ComplementHex { get; set; }
async void SetColor()
{
try
{
var color = Color.FromArgb(Hex);
RegularColor = color;
AlphaColor = color.WithAlpha(Alpha / 255f);
SaturationColor = color.WithSaturation(Saturation / 100f);
HueColor = color.WithHue(Hue / 255f);
LuminosityColor = color.WithLuminosity(Luminosity / 100f);
ComplementColor = color.GetComplementary();
ComplementHex = ComplementColor.ToHex();
OnPropertyChanged(nameof(RegularColor));
OnPropertyChanged(nameof(AlphaColor));
OnPropertyChanged(nameof(SaturationColor));
OnPropertyChanged(nameof(HueColor));
OnPropertyChanged(nameof(ComplementColor));
OnPropertyChanged(nameof(LuminosityColor));
OnPropertyChanged(nameof(ComplementHex));
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to convert colors: {ex.Message}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,107 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
class CompassViewModel : BaseViewModel
{
bool isActive;
bool applyLowPassFilter;
double heading;
int speed = 0;
public CompassViewModel()
{
StartCommand = new Command(OnStart);
StopCommand = new Command(OnStop);
}
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public bool IsActive
{
get => isActive;
set => SetProperty(ref isActive, value);
}
public bool ApplyLowPassFilter
{
get => applyLowPassFilter;
set => SetProperty(ref applyLowPassFilter, value);
}
public double Heading
{
get => heading;
set => SetProperty(ref heading, value);
}
public int Speed
{
get => speed;
set => SetProperty(ref speed, value);
}
public string[] Speeds { get; } =
Enum.GetNames(typeof(SensorSpeed));
public override void OnAppearing()
{
Compass.ReadingChanged += OnCompassReadingChanged;
base.OnAppearing();
}
public override void OnDisappearing()
{
OnStop();
Compass.ReadingChanged -= OnCompassReadingChanged;
base.OnDisappearing();
}
async void OnStart()
{
try
{
Compass.Start((SensorSpeed)Speed, ApplyLowPassFilter);
IsActive = true;
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to start compass: {ex.Message}");
}
}
void OnStop()
{
try
{
Compass.Stop();
IsActive = false;
}
catch (Exception ex)
{
Debug.WriteLine("Unable to stop compass: {0}", ex);
}
}
void OnCompassReadingChanged(object sender, CompassChangedEventArgs e)
{
switch ((SensorSpeed)Speed)
{
case SensorSpeed.Fastest:
case SensorSpeed.Game:
MainThread.BeginInvokeOnMainThread(() => Heading = e.Reading.HeadingMagneticNorth);
break;
default:
Heading = e.Reading.HeadingMagneticNorth;
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,40 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class ConnectivityViewModel : BaseViewModel
{
public string NetworkAccess =>
Connectivity.NetworkAccess.ToString();
public string ConnectionProfiles
{
get
{
var profiles = string.Empty;
foreach (var p in Connectivity.ConnectionProfiles)
profiles += "\n" + p.ToString();
return profiles;
}
}
public override void OnAppearing()
{
base.OnAppearing();
Connectivity.ConnectivityChanged += OnConnectivityChanged;
}
public override void OnDisappearing()
{
Connectivity.ConnectivityChanged -= OnConnectivityChanged;
base.OnDisappearing();
}
void OnConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
OnPropertyChanged(nameof(ConnectionProfiles));
OnPropertyChanged(nameof(NetworkAccess));
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
namespace PlatformIntegrationDemo.ViewModels
{
class ContactDetailsViewModel : BaseViewModel
{
public ContactDetailsViewModel(Contact contact)
{
Contact = contact;
}
public Contact Contact { get; }
}
}

Просмотреть файл

@ -0,0 +1,99 @@
using System.Collections.ObjectModel;
using System.Windows.Input;
using ContactsManager = Microsoft.Maui.ApplicationModel.Communication.Contacts;
namespace PlatformIntegrationDemo.ViewModels
{
class ContactsViewModel : BaseViewModel
{
Contact selectedContact;
public ContactsViewModel()
{
GetContactCommand = new Command(OnGetContact);
GetAllContactCommand = new Command(OnGetAllContact);
}
public ICommand GetContactCommand { get; }
public ICommand GetAllContactCommand { get; }
public ObservableCollection<Contact> ContactsList { get; } = new ObservableCollection<Contact>();
public Contact SelectedContact
{
get => selectedContact;
set => SetProperty(ref selectedContact, value, onChanged: OnContactSelected);
}
async void OnGetContact()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var contact = await ContactsManager.PickContactAsync();
if (contact == null)
return;
var details = new ContactDetailsViewModel(contact);
await NavigateAsync(details);
}
catch (Exception ex)
{
MainThread.BeginInvokeOnMainThread(async () => await DisplayAlertAsync($"Error:{ex.Message}"));
}
finally
{
IsBusy = false;
}
}
async void OnGetAllContact()
{
if (await Permissions.RequestAsync<Permissions.ContactsRead>() != PermissionStatus.Granted)
return;
if (IsBusy)
return;
IsBusy = true;
ContactsList.Clear();
try
{
var contacts = await ContactsManager.GetAllAsync();
await Task.Run(() =>
{
foreach (var contact in contacts)
{
MainThread.BeginInvokeOnMainThread(() => ContactsList.Add(contact));
}
});
}
catch (Exception ex)
{
await DisplayAlertAsync($"Error:{ex.Message}");
}
finally
{
IsBusy = false;
}
}
async void OnContactSelected()
{
if (SelectedContact == null)
return;
var details = new ContactDetailsViewModel(SelectedContact);
SelectedContact = null;
await NavigateAsync(details);
}
}
}

Просмотреть файл

@ -0,0 +1,60 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class DeviceInfoViewModel : BaseViewModel
{
DisplayInfo screenMetrics;
public string Model => DeviceInfo.Model;
public string Manufacturer => DeviceInfo.Manufacturer;
public string Name => DeviceInfo.Name;
public string VersionString => DeviceInfo.VersionString;
public string Version => DeviceInfo.Version.ToString();
public DevicePlatform Platform => DeviceInfo.Platform;
public DeviceIdiom Idiom => DeviceInfo.Idiom;
public DeviceType DeviceType => DeviceInfo.DeviceType;
public double Rotation =>
ScreenMetrics.Rotation switch
{
DisplayRotation.Rotation0 => 0,
DisplayRotation.Rotation90 => -90,
DisplayRotation.Rotation180 => -180,
DisplayRotation.Rotation270 => -270,
_ => 0
};
public DisplayInfo ScreenMetrics
{
get => screenMetrics;
set => SetProperty(ref screenMetrics, value, onChanged: () => OnPropertyChanged(nameof(Rotation)));
}
public override void OnAppearing()
{
base.OnAppearing();
DeviceDisplay.MainDisplayInfoChanged += OnScreenMetricsChanged;
ScreenMetrics = DeviceDisplay.MainDisplayInfo;
}
public override void OnDisappearing()
{
DeviceDisplay.MainDisplayInfoChanged -= OnScreenMetricsChanged;
base.OnDisappearing();
}
void OnScreenMetricsChanged(object sender, DisplayInfoChangedEventArgs e)
{
ScreenMetrics = e.DisplayInfo;
}
}
}

Просмотреть файл

@ -0,0 +1,127 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class EmailViewModel : BaseViewModel
{
string subject = "Hello World!";
string body = "This is the email body.\nWe hope you like it!";
string recipientsTo = "someone@example.org";
string recipientsCc;
string recipientsBcc;
string attachmentContents;
string attachmentName;
bool hasMultipleAttachements;
bool isHtml;
public EmailViewModel()
{
SendEmailCommand = new Command(OnSendEmail);
}
public ICommand SendEmailCommand { get; }
public string Subject
{
get => subject;
set => SetProperty(ref subject, value);
}
public string Body
{
get => body;
set => SetProperty(ref body, value);
}
public string RecipientsTo
{
get => recipientsTo;
set => SetProperty(ref recipientsTo, value);
}
public string RecipientsCc
{
get => recipientsCc;
set => SetProperty(ref recipientsCc, value);
}
public string RecipientsBcc
{
get => recipientsBcc;
set => SetProperty(ref recipientsBcc, value);
}
public string AttachmentContents
{
get => attachmentContents;
set => SetProperty(ref attachmentContents, value);
}
public string AttachmentName
{
get => attachmentName;
set => SetProperty(ref attachmentName, value);
}
public bool HasMultipleAttachements
{
get => hasMultipleAttachements;
set => SetProperty(ref hasMultipleAttachements, value);
}
public bool IsHtml
{
get => isHtml;
set => SetProperty(ref isHtml, value);
}
async void OnSendEmail()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var message = new EmailMessage
{
Subject = Subject,
Body = Body,
BodyFormat = isHtml ? EmailBodyFormat.Html : EmailBodyFormat.PlainText,
To = Split(RecipientsTo),
Cc = Split(RecipientsCc),
Bcc = Split(RecipientsBcc),
};
if (!string.IsNullOrWhiteSpace(AttachmentName) || !string.IsNullOrWhiteSpace(AttachmentContents))
{
// create a temprary file
var fn = string.IsNullOrWhiteSpace(AttachmentName) ? "Attachment.txt" : AttachmentName.Trim();
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, AttachmentContents);
message.Attachments.Add(new EmailAttachment(file));
if (HasMultipleAttachements)
{
fn = "1" + fn;
file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, AttachmentContents);
message.Attachments.Add(new EmailAttachment(file));
}
}
await Email.ComposeAsync(message);
}
finally
{
IsBusy = false;
}
}
List<string> Split(string recipients)
=> recipients?.Split(new char[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)?.ToList();
}
}

Просмотреть файл

@ -0,0 +1,218 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class FilePickerViewModel : BaseViewModel
{
string text;
ImageSource image;
bool isImageVisible;
public FilePickerViewModel()
{
PickFileCommand = new Command(() => DoPickFile());
PickImageCommand = new Command(() => DoPickImage());
PickPdfCommand = new Command(() => DoPickPdf());
PickCustomTypeCommand = new Command(() => DoPickCustomType());
PickAndSendCommand = new Command(() => DoPickAndSend());
PickMultipleFilesCommand = new Command(() => DoPickMultipleFiles());
}
public ICommand PickFileCommand { get; }
public ICommand PickImageCommand { get; }
public ICommand PickPdfCommand { get; }
public ICommand PickCustomTypeCommand { get; }
public ICommand PickAndSendCommand { get; }
public ICommand PickMultipleFilesCommand { get; }
public string Text
{
get => text;
set => SetProperty(ref text, value);
}
public ImageSource Image
{
get => image;
set => SetProperty(ref image, value);
}
public bool IsImageVisible
{
get => isImageVisible;
set => SetProperty(ref isImageVisible, value);
}
async void DoPickFile()
{
await PickAndShow(PickOptions.Default);
}
async void DoPickImage()
{
var options = new PickOptions
{
PickerTitle = "Please select an image",
FileTypes = FilePickerFileType.Images,
};
await PickAndShow(options);
}
async void DoPickPdf()
{
var options = new PickOptions
{
PickerTitle = "Please select a pdf",
FileTypes = FilePickerFileType.Pdf,
};
await PickAndShow(options);
}
async void DoPickCustomType()
{
var customFileType =
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "public.my.comic.extension" } }, // or general UTType values
{ DevicePlatform.Android, new[] { "application/comics" } },
{ DevicePlatform.WinUI, new[] { ".cbr", ".cbz" } },
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "cbr", "cbz" } }, // or general UTType values
});
var options = new PickOptions
{
PickerTitle = "Please select a comic file",
FileTypes = customFileType,
};
await PickAndShow(options);
}
async void DoPickAndSend()
{
// pick a file
var result = await PickAndShow(PickOptions.Images);
if (result == null)
return;
// copy it locally
var copyDir = FileSystem.CacheDirectory;
Directory.CreateDirectory(copyDir);
var copyPath = Path.Combine(copyDir, result.FileName);
using (var destination = File.Create(copyPath))
using (var source = await result.OpenReadAsync())
await source.CopyToAsync(destination);
// send it via an email
await Email.ComposeAsync(new EmailMessage
{
Subject = "Test Subject",
Body = "This is the body. There should be an image attached.",
Attachments =
{
new EmailAttachment(copyPath)
}
});
}
async Task<FileResult> PickAndShow(PickOptions options)
{
try
{
var result = await FilePicker.PickAsync(options);
if (result != null)
{
var size = await GetStreamSizeAsync(result);
Text = $"File Name: {result.FileName} ({size:0.00} KB)";
var ext = Path.GetExtension(result.FileName).ToLowerInvariant();
if (ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".gif")
{
var stream = await result.OpenReadAsync();
Image = ImageSource.FromStream(() => stream);
IsImageVisible = true;
}
else
{
IsImageVisible = false;
}
}
else
{
Text = $"Pick cancelled.";
}
return result;
}
catch (Exception ex)
{
Text = ex.ToString();
IsImageVisible = false;
return null;
}
}
async Task<double> GetStreamSizeAsync(FileResult result)
{
try
{
using var stream = await result.OpenReadAsync();
return stream.Length / 1024.0;
}
catch
{
return 0.0;
}
}
async void DoPickMultipleFiles()
{
try
{
var resultList = await FilePicker.PickMultipleAsync();
if (resultList != null && resultList.Any())
{
Text = "File Names: " + string.Join(", ", resultList.Select(result => result.FileName));
// only showing the first file's content
var firstResult = resultList.First();
if (firstResult.FileName.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ||
firstResult.FileName.EndsWith("png", StringComparison.OrdinalIgnoreCase))
{
var stream = await firstResult.OpenReadAsync();
Image = ImageSource.FromStream(() => stream);
IsImageVisible = true;
}
else
{
IsImageVisible = false;
}
}
else
{
Text = $"Pick cancelled.";
IsImageVisible = false;
}
}
catch (Exception ex)
{
Text = ex.ToString();
IsImageVisible = false;
}
}
}
}

Просмотреть файл

@ -0,0 +1,70 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class FileSystemViewModel : BaseViewModel
{
const string templateFileName = "FileSystemTemplate.txt";
const string localFileName = "TheFile.txt";
static string localPath = Path.Combine(FileSystem.AppDataDirectory, localFileName);
string currentContents;
public FileSystemViewModel()
{
LoadFileCommand = new Command(DoLoadFile);
SaveFileCommand = new Command(DoSaveFile);
DeleteFileCommand = new Command(DoDeleteFile);
DoLoadFile();
}
public ICommand LoadFileCommand { get; }
public ICommand SaveFileCommand { get; }
public ICommand DeleteFileCommand { get; }
public string AppDataDirectory => FileSystem.AppDataDirectory;
public string CacheDirectory => FileSystem.CacheDirectory;
public string CurrentContents
{
get => currentContents;
set => SetProperty(ref currentContents, value);
}
async void DoLoadFile()
{
if (File.Exists(localPath))
{
CurrentContents = File.ReadAllText(localPath);
}
else
{
using (var stream = await FileSystem.OpenAppPackageFileAsync(templateFileName))
using (var reader = new StreamReader(stream))
{
CurrentContents = await reader.ReadToEndAsync();
}
}
}
void DoSaveFile()
{
var dir = Path.GetDirectoryName(localPath);
Directory.CreateDirectory(dir);
File.WriteAllText(localPath, CurrentContents);
}
void DoDeleteFile()
{
if (File.Exists(localPath))
File.Delete(localPath);
}
}
}

Просмотреть файл

@ -0,0 +1,80 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class FlashlightViewModel : BaseViewModel
{
bool isOn;
bool isSupported = true;
public FlashlightViewModel()
{
ToggleCommand = new Command(OnToggle);
App.Current.Dispatcher.Dispatch(async () => await InitViewModel());
}
public ICommand ToggleCommand { get; }
public bool IsOn
{
get => isOn;
set => SetProperty(ref isOn, value);
}
public bool IsSupported
{
get => isSupported;
set => SetProperty(ref isSupported, value);
}
async Task InitViewModel()
{
IsSupported = await Flashlight.IsSupportedAsync();
}
public override void OnDisappearing()
{
if (!IsOn)
return;
try
{
Flashlight.TurnOffAsync();
IsOn = false;
}
catch (FeatureNotSupportedException)
{
IsSupported = false;
}
base.OnDisappearing();
}
async void OnToggle()
{
try
{
if (IsOn)
{
await Flashlight.TurnOffAsync();
IsOn = false;
}
else
{
await Flashlight.TurnOnAsync();
IsOn = true;
}
}
catch (FeatureNotSupportedException fnsEx)
{
IsSupported = false;
await DisplayAlertAsync($"Unable toggle flashlight: {fnsEx.Message}");
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable toggle flashlight: {ex.Message}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,127 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class GeocodingViewModel : BaseViewModel
{
string lat = 47.67398.ToString();
string lon = (-122.121513).ToString();
string address = "Microsoft Building 25 Redmond WA";
string geocodeAddress;
string geocodePosition;
public GeocodingViewModel()
{
GetAddressCommand = new Command(OnGetAddress);
GetPositionCommand = new Command(OnGetPosition);
}
public ICommand GetAddressCommand { get; }
public ICommand GetPositionCommand { get; }
public string Latitude
{
get => lat;
set => SetProperty(ref lat, value);
}
public string Longitude
{
get => lon;
set => SetProperty(ref lon, value);
}
public string GeocodeAddress
{
get => geocodeAddress;
set => SetProperty(ref geocodeAddress, value);
}
public string Address
{
get => address;
set => SetProperty(ref address, value);
}
public string GeocodePosition
{
get => geocodePosition;
set => SetProperty(ref geocodePosition, value);
}
async void OnGetPosition()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var locations = await Geocoding.GetLocationsAsync(Address);
var location = locations?.FirstOrDefault();
if (location == null)
{
GeocodePosition = "Unable to detect locations";
}
else
{
GeocodePosition =
$"{nameof(location.Latitude)}: {location.Latitude}\n" +
$"{nameof(location.Longitude)}: {location.Longitude}\n";
}
}
catch (Exception ex)
{
GeocodePosition = $"Unable to detect locations: {ex.Message}";
}
finally
{
IsBusy = false;
}
}
async void OnGetAddress()
{
if (IsBusy)
return;
IsBusy = true;
try
{
double.TryParse(lat, out var lt);
double.TryParse(lon, out var ln);
var placemarks = await Geocoding.GetPlacemarksAsync(lt, ln);
var placemark = placemarks?.FirstOrDefault();
if (placemark == null)
{
GeocodeAddress = "Unable to detect placemarks.";
}
else
{
GeocodeAddress =
$"{nameof(placemark.AdminArea)}: {placemark.AdminArea}\n" +
$"{nameof(placemark.CountryCode)}: {placemark.CountryCode}\n" +
$"{nameof(placemark.CountryName)}: {placemark.CountryName}\n" +
$"{nameof(placemark.FeatureName)}: {placemark.FeatureName}\n" +
$"{nameof(placemark.Locality)}: {placemark.Locality}\n" +
$"{nameof(placemark.PostalCode)}: {placemark.PostalCode}\n" +
$"{nameof(placemark.SubAdminArea)}: {placemark.SubAdminArea}\n" +
$"{nameof(placemark.SubLocality)}: {placemark.SubLocality}\n" +
$"{nameof(placemark.SubThoroughfare)}: {placemark.SubThoroughfare}\n" +
$"{nameof(placemark.Thoroughfare)}: {placemark.Thoroughfare}\n";
}
}
catch (Exception ex)
{
GeocodeAddress = $"Unable to detect placemarks: {ex.Message}";
}
finally
{
IsBusy = false;
}
}
}
}

Просмотреть файл

@ -0,0 +1,193 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class GeolocationViewModel : BaseViewModel
{
string notAvailable = "not available";
string lastLocation;
string currentLocation;
int accuracy = (int)GeolocationAccuracy.Default;
CancellationTokenSource cts;
string listeningLocation;
string listeningLocationStatus;
public GeolocationViewModel()
{
GetLastLocationCommand = new Command(OnGetLastLocation);
GetCurrentLocationCommand = new Command(OnGetCurrentLocation);
StartListeningCommand = new Command(OnStartListening);
StopListeningCommand = new Command(OnStopListening);
}
public ICommand GetLastLocationCommand { get; }
public ICommand GetCurrentLocationCommand { get; }
public ICommand StartListeningCommand { get; }
public ICommand StopListeningCommand { get; }
public string LastLocation
{
get => lastLocation;
set => SetProperty(ref lastLocation, value);
}
public string CurrentLocation
{
get => currentLocation;
set => SetProperty(ref currentLocation, value);
}
public string[] Accuracies
=> Enum.GetNames(typeof(GeolocationAccuracy));
public int Accuracy
{
get => accuracy;
set => SetProperty(ref accuracy, value);
}
public bool IsListening => Geolocation.IsListeningForeground;
public bool IsNotListening => !IsListening;
public string ListeningLocation
{
get => listeningLocation;
set => SetProperty(ref listeningLocation, value);
}
public string ListeningLocationStatus
{
get => listeningLocationStatus;
set => SetProperty(ref listeningLocationStatus, value);
}
async void OnGetLastLocation()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var location = await Geolocation.GetLastKnownLocationAsync();
LastLocation = FormatLocation(location);
}
catch (Exception ex)
{
LastLocation = FormatLocation(null, ex);
}
IsBusy = false;
}
async void OnGetCurrentLocation()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var request = new GeolocationRequest((GeolocationAccuracy)Accuracy);
cts = new CancellationTokenSource();
var location = await Geolocation.GetLocationAsync(request, cts.Token);
CurrentLocation = FormatLocation(location);
}
catch (Exception ex)
{
CurrentLocation = FormatLocation(null, ex);
}
finally
{
cts.Dispose();
cts = null;
}
IsBusy = false;
}
async void OnStartListening()
{
try
{
Geolocation.LocationChanged += Geolocation_LocationChanged;
var request = new GeolocationListeningRequest((GeolocationAccuracy)Accuracy);
var success = await Geolocation.StartListeningForegroundAsync(request);
ListeningLocationStatus = success
? "Started listening for foreground location updates"
: "Couldn't start listening";
}
catch (Exception ex)
{
ListeningLocationStatus = FormatLocation(null, ex);
}
OnPropertyChanged(nameof(IsListening));
OnPropertyChanged(nameof(IsNotListening));
}
void Geolocation_LocationChanged(object sender, GeolocationLocationChangedEventArgs e)
{
ListeningLocation = FormatLocation(e.Location);
}
void OnStopListening()
{
try
{
Geolocation.LocationChanged -= Geolocation_LocationChanged;
Geolocation.StopListeningForeground();
ListeningLocationStatus = "Stopped listening for foreground location updates";
}
catch (Exception ex)
{
ListeningLocationStatus = FormatLocation(null, ex);
}
OnPropertyChanged(nameof(IsListening));
OnPropertyChanged(nameof(IsNotListening));
}
string FormatLocation(Location location, Exception ex = null)
{
if (location == null)
{
return $"Unable to detect location. Exception: {ex?.Message ?? string.Empty}";
}
return
$"Latitude: {location.Latitude}\n" +
$"Longitude: {location.Longitude}\n" +
$"HorizontalAccuracy: {location.Accuracy}\n" +
$"Altitude: {(location.Altitude.HasValue ? location.Altitude.Value.ToString() : notAvailable)}\n" +
$"AltitudeRefSys: {location.AltitudeReferenceSystem.ToString()}\n" +
$"VerticalAccuracy: {(location.VerticalAccuracy.HasValue ? location.VerticalAccuracy.Value.ToString() : notAvailable)}\n" +
$"Heading: {(location.Course.HasValue ? location.Course.Value.ToString() : notAvailable)}\n" +
$"Speed: {(location.Speed.HasValue ? location.Speed.Value.ToString() : notAvailable)}\n" +
$"Date (UTC): {location.Timestamp:d}\n" +
$"Time (UTC): {location.Timestamp:T}\n" +
$"Mocking Provider: {location.IsFromMockProvider}";
}
public override void OnDisappearing()
{
if (IsBusy)
{
if (cts != null && !cts.IsCancellationRequested)
cts.Cancel();
}
OnStopListening();
base.OnDisappearing();
}
}
}

Просмотреть файл

@ -0,0 +1,122 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class GyroscopeViewModel : BaseViewModel
{
double x;
double y;
double z;
bool isActive;
int speed = 0;
public GyroscopeViewModel()
{
StartCommand = new Command(OnStart);
StopCommand = new Command(OnStop);
}
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public double X
{
get => x;
set => SetProperty(ref x, value);
}
public double Y
{
get => y;
set => SetProperty(ref y, value);
}
public double Z
{
get => z;
set => SetProperty(ref z, value);
}
public bool IsActive
{
get => isActive;
set => SetProperty(ref isActive, value);
}
public string[] Speeds { get; } =
Enum.GetNames(typeof(SensorSpeed));
public int Speed
{
get => speed;
set => SetProperty(ref speed, value);
}
public override void OnAppearing()
{
Gyroscope.ReadingChanged += OnReadingChanged;
base.OnAppearing();
}
public override void OnDisappearing()
{
OnStop();
Gyroscope.ReadingChanged -= OnReadingChanged;
base.OnDisappearing();
}
async void OnStart()
{
try
{
Gyroscope.Start((SensorSpeed)Speed);
IsActive = true;
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to start gyroscope: {ex.Message}");
}
}
void OnStop()
{
try
{
Gyroscope.Stop();
IsActive = false;
}
catch (Exception ex)
{
Debug.WriteLine("Unable to stop gyroscope: {0}", ex);
}
}
void OnReadingChanged(object sender, GyroscopeChangedEventArgs e)
{
var data = e.Reading;
switch ((SensorSpeed)Speed)
{
case SensorSpeed.Fastest:
case SensorSpeed.Game:
MainThread.BeginInvokeOnMainThread(() =>
{
X = data.AngularVelocity.X;
Y = data.AngularVelocity.Y;
Z = data.AngularVelocity.Z;
});
break;
default:
X = data.AngularVelocity.X;
Y = data.AngularVelocity.Y;
Z = data.AngularVelocity.Z;
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class HapticFeedbackViewModel : BaseViewModel
{
bool isSupported = true;
public HapticFeedbackViewModel()
{
ClickCommand = new Command(OnClick);
LongPressCommand = new Command(OnLongPress);
}
public ICommand ClickCommand { get; }
public ICommand LongPressCommand { get; }
public bool IsSupported
{
get => isSupported;
set => SetProperty(ref isSupported, value);
}
void OnClick()
{
try
{
HapticFeedback.Perform(HapticFeedbackType.Click);
}
catch (FeatureNotSupportedException)
{
IsSupported = false;
}
catch (Exception ex)
{
DisplayAlertAsync($"Unable to HapticFeedback: {ex.Message}");
}
}
void OnLongPress()
{
try
{
HapticFeedback.Perform(HapticFeedbackType.LongPress);
}
catch (FeatureNotSupportedException)
{
IsSupported = false;
}
catch (Exception ex)
{
DisplayAlertAsync($"Unable to HapticFeedback: {ex.Message}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class KeepScreenOnViewModel : BaseViewModel
{
public KeepScreenOnViewModel()
{
RequestActiveCommand = new Command(OnRequestActive);
RequestReleaseCommand = new Command(OnRequestRelease);
}
public bool IsActive => DeviceDisplay.KeepScreenOn;
public ICommand RequestActiveCommand { get; }
public ICommand RequestReleaseCommand { get; }
public override void OnDisappearing()
{
OnRequestRelease();
base.OnDisappearing();
}
void OnRequestActive()
{
DeviceDisplay.KeepScreenOn = true;
OnPropertyChanged(nameof(IsActive));
}
void OnRequestRelease()
{
DeviceDisplay.KeepScreenOn = false;
OnPropertyChanged(nameof(IsActive));
}
}
}

Просмотреть файл

@ -0,0 +1,99 @@
using PlatformIntegrationDemo.Helpers;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class LauncherViewModel : BaseViewModel
{
string fileAttachmentName;
string fileAttachmentContents;
public string LaunchUri { get; set; }
public ICommand LaunchCommand { get; }
public ICommand CanLaunchCommand { get; }
public ICommand LaunchMailCommand { get; }
public ICommand LaunchBrowserCommand { get; }
public ICommand LaunchFileCommand { get; }
public LauncherViewModel()
{
LaunchCommand = new Command(OnLaunch);
LaunchMailCommand = new Command(OnLaunchMail);
LaunchBrowserCommand = new Command(OnLaunchBrowser);
CanLaunchCommand = new Command(CanLaunch);
LaunchFileCommand = new Command<Microsoft.Maui.Controls.View>(OnFileRequest);
}
public string FileAttachmentContents
{
get => fileAttachmentContents;
set => SetProperty(ref fileAttachmentContents, value);
}
public string FileAttachmentName
{
get => fileAttachmentName;
set => SetProperty(ref fileAttachmentName, value);
}
async void OnLaunchBrowser()
{
await Launcher.OpenAsync("https://github.com/dotnet/maui");
}
async void OnLaunch()
{
try
{
await Launcher.OpenAsync(LaunchUri);
}
catch (Exception ex)
{
await DisplayAlertAsync($"Uri {LaunchUri} could not be launched: {ex}");
}
}
async void OnLaunchMail()
{
await Launcher.OpenAsync("mailto:");
}
async void CanLaunch()
{
try
{
var canBeLaunched = await Launcher.CanOpenAsync(LaunchUri);
await DisplayAlertAsync($"Uri {LaunchUri} can be launched: {canBeLaunched}");
}
catch (Exception ex)
{
await DisplayAlertAsync($"Uri {LaunchUri} could not be verified as launchable: {ex}");
}
}
async void OnFileRequest(Microsoft.Maui.Controls.View element)
{
if (!string.IsNullOrWhiteSpace(FileAttachmentContents))
{
// create a temprary file
var fn = string.IsNullOrWhiteSpace(FileAttachmentName) ? "Attachment.txt" : FileAttachmentName.Trim();
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, FileAttachmentContents);
var rect = element.GetAbsoluteBounds();
rect.Y += 40;
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(file),
PresentationSourceBounds = rect
});
}
}
}
}

Просмотреть файл

@ -0,0 +1,122 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class MagnetometerViewModel : BaseViewModel
{
double x;
double y;
double z;
bool isActive;
int speed = 0;
public MagnetometerViewModel()
{
StartCommand = new Command(OnStart);
StopCommand = new Command(OnStop);
}
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public double X
{
get => x;
set => SetProperty(ref x, value);
}
public double Y
{
get => y;
set => SetProperty(ref y, value);
}
public double Z
{
get => z;
set => SetProperty(ref z, value);
}
public bool IsActive
{
get => isActive;
set => SetProperty(ref isActive, value);
}
public string[] Speeds { get; } =
Enum.GetNames(typeof(SensorSpeed));
public int Speed
{
get => speed;
set => SetProperty(ref speed, value);
}
public override void OnAppearing()
{
Magnetometer.ReadingChanged += OnReadingChanged;
base.OnAppearing();
}
public override void OnDisappearing()
{
OnStop();
Magnetometer.ReadingChanged -= OnReadingChanged;
base.OnDisappearing();
}
async void OnStart()
{
try
{
Magnetometer.Start((SensorSpeed)Speed);
IsActive = true;
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to start magnetometer: {ex.Message}");
}
}
void OnStop()
{
try
{
Magnetometer.Stop();
IsActive = false;
}
catch (Exception ex)
{
Debug.WriteLine("Unable to stop magnetometer: {0}", ex);
}
}
void OnReadingChanged(object sender, MagnetometerChangedEventArgs e)
{
var data = e.Reading;
switch ((SensorSpeed)Speed)
{
case SensorSpeed.Fastest:
case SensorSpeed.Game:
MainThread.BeginInvokeOnMainThread(() =>
{
X = data.MagneticField.X;
Y = data.MagneticField.Y;
Z = data.MagneticField.Z;
});
break;
default:
X = data.MagneticField.X;
Y = data.MagneticField.Y;
Z = data.MagneticField.Z;
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,302 @@
using PlatformIntegrationDemo.Models;
using PlatformIntegrationDemo.Views;
namespace PlatformIntegrationDemo.ViewModels
{
public class MainPageViewModel : BaseViewModel
{
bool alreadyAppeared;
SampleItem[] samples;
IEnumerable<SampleItem> filteredItems;
string filterText;
public MainPageViewModel()
{
alreadyAppeared = false;
samples = new SampleItem[]
{
new SampleItem(
"📏",
"Accelerometer",
typeof(AccelerometerPage),
"Retrieve acceleration data of the device in 3D space.",
new[] { "accelerometer", "sensors", "hardware", "device" }),
new SampleItem(
"📏",
"All Sensors",
typeof(AllSensorsPage),
"Have a look at the accelerometer, barometer, compass, gyroscope, magnetometer, and orientation sensors.",
new[] { "accelerometer", "barometer", "compass", "gyroscope", "magnetometer", "orientation", "sensors", "hardware", "device" }),
new SampleItem(
"📦",
"App Info",
typeof(AppInfoPage),
"Find out about the app with ease.",
new[] { "app", "info" }),
new SampleItem(
"📏",
"Barometer",
typeof(BarometerPage),
"Easily detect pressure level, via the device barometer.",
new[] { "barometer", "hardware", "device", "sensor" }),
new SampleItem(
"🔋",
"Battery",
typeof(BatteryPage),
"Easily detect battery level, source, and state.",
new[] { "battery", "hardware", "device" }),
new SampleItem(
"🌐",
"Browser",
typeof(BrowserPage),
"Quickly and easily open a browser to a specific website.",
new[] { "browser", "web", "internet" }),
new SampleItem(
"📋",
"Clipboard",
typeof(ClipboardPage),
"Quickly and easily use the clipboard.",
new[] { "clipboard", "copy", "paste" }),
new SampleItem(
"🎨",
"Color Converters",
typeof(ColorConvertersPage),
"Convert and adjust colors.",
new[] { "color", "drawing", "style" }),
new SampleItem(
"🧭",
"Compass",
typeof(CompassPage),
"Monitor compass for changes.",
new[] { "compass", "sensors", "hardware", "device" }),
new SampleItem(
"📶",
"Connectivity",
typeof(ConnectivityPage),
"Check connectivity state and detect changes.",
new[] { "connectivity", "internet", "wifi" }),
new SampleItem(
"👶",
"Contacts",
typeof(ContactsPage),
"Get and add contacts in your device.",
new[] { "contacts", "people", "device" }),
new SampleItem(
"📱",
"Device Info",
typeof(DeviceInfoPage),
"Find out about the device with ease.",
new[] { "hardware", "device", "info", "screen", "display", "orientation", "rotation" }),
new SampleItem(
"📧",
"Email",
typeof(EmailPage),
"Easily send email messages.",
new[] { "email", "share", "communication", "message" }),
new SampleItem(
"📁",
"File Picker",
typeof(FilePickerPage),
"Easily pick files from storage.",
new[] { "files", "picking", "filesystem", "storage" }),
new SampleItem(
"📁",
"File System",
typeof(FileSystemPage),
"Easily save files to app data.",
new[] { "files", "directory", "filesystem", "storage" }),
new SampleItem(
"🔦",
"Flashlight",
typeof(FlashlightPage),
"A simple way to turn the flashlight on/off.",
new[] { "flashlight", "torch", "hardware", "flash", "device" }),
new SampleItem(
"📍",
"Geocoding",
typeof(GeocodingPage),
"Easily geocode and reverse geocoding.",
new[] { "geocoding", "geolocation", "position", "address", "mapping" }),
new SampleItem(
"📍",
"Geolocation",
typeof(GeolocationPage),
"Quickly get the current location.",
new[] { "geolocation", "position", "address", "mapping" }),
new SampleItem(
"💤",
"Keep Screen On",
typeof(KeepScreenOnPage),
"Keep the device screen awake.",
new[] { "screen", "awake", "sleep" }),
new SampleItem(
"📏",
"Launcher",
typeof(LauncherPage),
"Launch other apps via Uri",
new[] { "launcher", "app", "run" }),
new SampleItem(
"📏",
"Gyroscope",
typeof(GyroscopePage),
"Retrieve rotation around the device's three primary axes.",
new[] { "gyroscope", "sensors", "hardware", "device" }),
new SampleItem(
"📏",
"Magnetometer",
typeof(MagnetometerPage),
"Detect device's orientation relative to Earth's magnetic field.",
new[] { "compass", "magnetometer", "sensors", "hardware", "device" }),
new SampleItem(
"🗺",
"Launch Maps",
typeof(MapsPage),
"Easily launch maps with coordinates.",
new[] { "geocoding", "geolocation", "position", "address", "mapping", "maps", "route", "navigation" }),
new SampleItem(
"📏",
"Orientation Sensor",
typeof(OrientationSensorPage),
"Retrieve orientation of the device in 3D space.",
new[] { "orientation", "sensors", "hardware", "device" }),
new SampleItem(
"📷",
"Media Picker",
typeof(MediaPickerPage),
"Pick or capture a photo or video.",
new[] { "media", "picker", "video", "picture", "photo", "image", "movie" }),
new SampleItem(
"🔒",
"Permissions",
typeof(PermissionsPage),
"Request various permissions.",
new[] { "permissions" }),
new SampleItem(
"📞",
"Phone Dialer",
typeof(PhoneDialerPage),
"Easily open the phone dialer.",
new[] { "phone", "dialer", "communication", "call" }),
new SampleItem(
"⚙️",
"Preferences",
typeof(PreferencesPage),
"Quickly and easily add persistent preferences.",
new[] { "settings", "preferences", "prefs", "storage" }),
new SampleItem(
"📷",
"Screenshot",
typeof(ScreenshotPage),
"Quickly and easily take screenshots of your app.",
new[] { "screenshot", "picture", "media", "display" }),
new SampleItem(
"🔒",
"Secure Storage",
typeof(SecureStoragePage),
"Securely store data.",
new[] { "settings", "preferences", "prefs", "security", "storage" }),
new SampleItem(
"🐊",
"Semantic Screen Reader",
typeof(SemanticScreenReaderPage),
"Read out the semanic contents of a screen.",
new[] { "accessibility", "a11y", "screen reader", "semantic" }),
new SampleItem(
"📲",
"Share",
typeof(SharePage),
"Send text, website uris and files to other apps.",
new[] { "data", "transfer", "share", "communication" }),
new SampleItem(
"💬",
"SMS",
typeof(SMSPage),
"Easily send SMS messages.",
new[] { "sms", "message", "text", "communication", "share" }),
new SampleItem(
"🔊",
"Text To Speech",
typeof(TextToSpeechPage),
"Vocalize text on the device.",
new[] { "text", "message", "speech", "communication" }),
new SampleItem(
"🌡",
"Unit Converters",
typeof(UnitConvertersPage),
"Easily converter different units.",
new[] { "units", "converters", "calculations" }),
new SampleItem(
"📳",
"Vibration",
typeof(VibrationPage),
"Quickly and easily make the device vibrate.",
new[] { "vibration", "vibrate", "hardware", "device" }),
new SampleItem(
"📳",
"Haptic Feedback",
typeof(HapticFeedbackPage),
"Quickly and easily make the device provide haptic feedback",
new[] { "haptic", "feedback", "hardware", "device" }),
new SampleItem(
"🔓",
"Web Authenticator",
typeof(WebAuthenticatorPage),
"Quickly and easily authenticate and wait for a callback.",
new[] { "auth", "authenticate", "authenticator", "web", "webauth" }),
};
filteredItems = samples;
filterText = string.Empty;
}
public IEnumerable<SampleItem> FilteredItems
{
get => filteredItems;
private set => SetProperty(ref filteredItems, value);
}
public string FilterText
{
get => filterText;
set
{
SetProperty(ref filterText, value);
FilteredItems = Filter(samples, value);
}
}
public override void OnAppearing()
{
base.OnAppearing();
if (!alreadyAppeared)
{
alreadyAppeared = true;
if (VersionTracking.IsFirstLaunchEver)
{
DisplayAlertAsync("Welcome to the sample! This is the first time you are launching the app!");
}
else if (VersionTracking.IsFirstLaunchForCurrentVersion)
{
var count = VersionTracking.VersionHistory.Count();
DisplayAlertAsync($"Welcome to the NEW sample! You have tried {count} versions.");
}
}
}
static IEnumerable<SampleItem> Filter(IEnumerable<SampleItem> samples, string filterText)
{
if (!string.IsNullOrWhiteSpace(filterText))
{
samples = samples.Where(s =>
{
var tags = s.Tags.Union(new[] { s.Name });
return tags.Any(t => t.Contains(filterText, StringComparison.OrdinalIgnoreCase));
});
}
return samples.OrderBy(s => s.Name);
}
}
}

Просмотреть файл

@ -0,0 +1,125 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class MapsViewModel : BaseViewModel
{
string name = "Microsoft Building 25";
string longitude = (-122.130603).ToString();
string latitude = 47.645160.ToString();
string locality = "Redmond";
string adminArea = "WA";
string thoroughfare = "Microsoft Building 25";
string country = "United States";
string zipCode = "98052";
int navigationMode;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
public string Longitude
{
get => longitude;
set => SetProperty(ref longitude, value);
}
public string Latitude
{
get => latitude;
set => SetProperty(ref latitude, value);
}
public string Locality
{
get => locality;
set => SetProperty(ref locality, value);
}
public string AdminArea
{
get => adminArea;
set => SetProperty(ref adminArea, value);
}
public string Thoroughfare
{
get => thoroughfare;
set => SetProperty(ref thoroughfare, value);
}
public string Country
{
get => country;
set => SetProperty(ref country, value);
}
public string ZipCode
{
get => zipCode;
set => SetProperty(ref zipCode, value);
}
public string[] NavigationModes { get; } =
Enum.GetNames(typeof(NavigationMode));
public int NavigationMode
{
get => navigationMode;
set => SetProperty(ref navigationMode, value);
}
public ICommand MapsCommand { get; }
public ICommand LaunchPlacemarkCommand { get; }
public MapsViewModel()
{
MapsCommand = new Command(OpenLocation);
LaunchPlacemarkCommand = new Command(OpenPlacemark);
}
async void OpenLocation()
{
var canOpen = await Map.TryOpenAsync(
double.Parse(Latitude),
double.Parse(Longitude),
new MapLaunchOptions
{
Name = Name,
NavigationMode = (NavigationMode)NavigationMode
});
if (!canOpen)
{
await DisplayAlertAsync("Unable to open map, possibly due to the fact that there is no default maps app installed.");
}
}
async void OpenPlacemark()
{
var placemark = new Placemark
{
Locality = Locality,
AdminArea = AdminArea,
CountryName = Country,
Thoroughfare = Thoroughfare,
PostalCode = ZipCode
};
var canOpen = await Map.TryOpenAsync(placemark, new MapLaunchOptions
{
Name = Name,
NavigationMode = (NavigationMode)NavigationMode
});
if (!canOpen)
{
await DisplayAlertAsync("Unable to open map, possibly due to the fact that there is no default maps app installed.");
}
}
}
}

Просмотреть файл

@ -0,0 +1,171 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class MediaPickerViewModel : BaseViewModel
{
string photoPath;
string videoPath;
bool showPhoto;
bool showVideo;
public MediaPickerViewModel()
{
PickPhotoCommand = new Command(DoPickPhoto);
CapturePhotoCommand = new Command(DoCapturePhoto, () => MediaPicker.IsCaptureSupported);
PickVideoCommand = new Command(DoPickVideo);
CaptureVideoCommand = new Command(DoCaptureVideo, () => MediaPicker.IsCaptureSupported);
}
public ICommand PickPhotoCommand { get; }
public ICommand CapturePhotoCommand { get; }
public ICommand PickVideoCommand { get; }
public ICommand CaptureVideoCommand { get; }
public bool ShowPhoto
{
get => showPhoto;
set => SetProperty(ref showPhoto, value);
}
public bool ShowVideo
{
get => showVideo;
set => SetProperty(ref showVideo, value);
}
public string PhotoPath
{
get => photoPath;
set => SetProperty(ref photoPath, value);
}
public string VideoPath
{
get => videoPath;
set => SetProperty(ref videoPath, value);
}
async void DoPickPhoto()
{
try
{
var photo = await MediaPicker.PickPhotoAsync();
await LoadPhotoAsync(photo);
Console.WriteLine($"PickPhotoAsync COMPLETED: {PhotoPath}");
}
catch (Exception ex)
{
Console.WriteLine($"PickPhotoAsync THREW: {ex.Message}");
}
}
async void DoCapturePhoto()
{
try
{
var photo = await MediaPicker.CapturePhotoAsync();
await LoadPhotoAsync(photo);
Console.WriteLine($"CapturePhotoAsync COMPLETED: {PhotoPath}");
}
catch (Exception ex)
{
Console.WriteLine($"CapturePhotoAsync THREW: {ex.Message}");
}
}
async void DoPickVideo()
{
try
{
var video = await MediaPicker.PickVideoAsync();
await LoadVideoAsync(video);
Console.WriteLine($"PickVideoAsync COMPLETED: {PhotoPath}");
}
catch (Exception ex)
{
Console.WriteLine($"PickVideoAsync THREW: {ex.Message}");
}
}
async void DoCaptureVideo()
{
try
{
var photo = await MediaPicker.CaptureVideoAsync();
await LoadVideoAsync(photo);
Console.WriteLine($"CaptureVideoAsync COMPLETED: {VideoPath}");
}
catch (Exception ex)
{
Console.WriteLine($"CaptureVideoAsync THREW: {ex.Message}");
}
}
async Task LoadPhotoAsync(FileResult photo)
{
// canceled
if (photo == null)
{
PhotoPath = null;
return;
}
// save the file into local storage
var newFile = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
using (var stream = await photo.OpenReadAsync())
using (var newStream = File.OpenWrite(newFile))
{
await stream.CopyToAsync(newStream);
}
PhotoPath = newFile;
ShowVideo = false;
ShowPhoto = true;
}
async Task LoadVideoAsync(FileResult video)
{
// canceled
if (video == null)
{
VideoPath = null;
return;
}
// save the file into local storage
var newFile = Path.Combine(FileSystem.CacheDirectory, video.FileName);
using (var stream = await video.OpenReadAsync())
using (var newStream = File.OpenWrite(newFile))
{
await stream.CopyToAsync(newStream);
}
VideoPath = newFile;
ShowVideo = true;
ShowPhoto = false;
}
public override void OnDisappearing()
{
PhotoPath = null;
VideoPath = null;
base.OnDisappearing();
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace PlatformIntegrationDemo.ViewModels
{
public class ObservableObject : INotifyPropertyChanged
{
protected virtual bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "", Action onChanged = null, Func<T, T, bool> validateValue = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
if (validateValue != null && !validateValue(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
protected virtual bool SetProperty<T>(T originalValue, T value, Action onChanged, [CallerMemberName] string propertyName = "", Func<T, T, bool> validateValue = null)
{
if (EqualityComparer<T>.Default.Equals(originalValue, value))
return false;
if (validateValue != null && !validateValue(originalValue, value))
return false;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Просмотреть файл

@ -0,0 +1,131 @@
using System.Diagnostics;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
class OrientationSensorViewModel : BaseViewModel
{
double x;
double y;
double z;
double w;
bool isActive;
int speed = 0;
public OrientationSensorViewModel()
{
StartCommand = new Command(OnStart);
StopCommand = new Command(OnStop);
}
public ICommand StartCommand { get; }
public ICommand StopCommand { get; }
public double X
{
get => x;
set => SetProperty(ref x, value);
}
public double Y
{
get => y;
set => SetProperty(ref y, value);
}
public double Z
{
get => z;
set => SetProperty(ref z, value);
}
public double W
{
get => w;
set => SetProperty(ref w, value);
}
public bool IsActive
{
get => isActive;
set => SetProperty(ref isActive, value);
}
public string[] Speeds { get; } =
Enum.GetNames(typeof(SensorSpeed));
public int Speed
{
get => speed;
set => SetProperty(ref speed, value);
}
public override void OnAppearing()
{
OrientationSensor.ReadingChanged += OnReadingChanged;
base.OnAppearing();
}
public override void OnDisappearing()
{
OnStop();
OrientationSensor.ReadingChanged -= OnReadingChanged;
base.OnDisappearing();
}
async void OnStart()
{
try
{
OrientationSensor.Start((SensorSpeed)Speed);
IsActive = true;
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to start orientation sensor: {ex.Message}");
}
}
void OnStop()
{
try
{
OrientationSensor.Stop();
IsActive = false;
}
catch (Exception ex)
{
Debug.WriteLine("Unable to stop orientation sensor: {0}", ex);
}
}
void OnReadingChanged(object sender, OrientationSensorChangedEventArgs e)
{
var data = e.Reading;
switch ((SensorSpeed)Speed)
{
case SensorSpeed.Fastest:
case SensorSpeed.Game:
MainThread.BeginInvokeOnMainThread(() =>
{
X = data.Orientation.X;
Y = data.Orientation.Y;
Z = data.Orientation.Z;
W = data.Orientation.W;
});
break;
default:
X = data.Orientation.X;
Y = data.Orientation.Y;
Z = data.Orientation.Z;
W = data.Orientation.W;
break;
}
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
using PlatformIntegrationDemo.Models;
namespace PlatformIntegrationDemo.ViewModels
{
public class PermissionsViewModel : BaseViewModel
{
public List<PermissionItem> PermissionItems =>
new List<PermissionItem>
{
new PermissionItem("Battery", new Permissions.Battery()),
new PermissionItem("Calendar (Read)", new Permissions.CalendarRead()),
new PermissionItem("Calendar (Write)", new Permissions.CalendarWrite()),
new PermissionItem("Camera", new Permissions.Camera()),
new PermissionItem("Contacts (Read)", new Permissions.ContactsRead()),
new PermissionItem("Contacts (Write)", new Permissions.ContactsWrite()),
new PermissionItem("Flashlight", new Permissions.Flashlight()),
new PermissionItem("Launch Apps", new Permissions.LaunchApp()),
new PermissionItem("Location (Always)", new Permissions.LocationAlways()),
new PermissionItem("Location (Only When In Use)", new Permissions.LocationWhenInUse()),
new PermissionItem("Maps", new Permissions.Maps()),
new PermissionItem("Media Library", new Permissions.Media()),
new PermissionItem("Microphone", new Permissions.Microphone()),
new PermissionItem("Network State", new Permissions.NetworkState()),
new PermissionItem("Phone", new Permissions.Phone()),
new PermissionItem("Photos", new Permissions.Photos()),
new PermissionItem("Photos AddOnly", new Permissions.PhotosAddOnly()),
new PermissionItem("Reminders", new Permissions.Reminders()),
new PermissionItem("Sensors", new Permissions.Sensors()),
new PermissionItem("SMS", new Permissions.Sms()),
new PermissionItem("Speech", new Permissions.Speech()),
new PermissionItem("Storage (Read)", new Permissions.StorageRead()),
new PermissionItem("Storage (Write)", new Permissions.StorageWrite()),
new PermissionItem("Vibrate", new Permissions.Vibrate())
};
}
}

Просмотреть файл

@ -0,0 +1,35 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class PhoneDialerViewModel : BaseViewModel
{
string phoneNumber;
public PhoneDialerViewModel()
{
OpenPhoneDialerCommand = new Command(OnOpenPhoneDialer);
}
public ICommand OpenPhoneDialerCommand { get; }
public string PhoneNumber
{
get => phoneNumber;
set => SetProperty(ref phoneNumber, value);
}
async void OnOpenPhoneDialer()
{
try
{
PhoneDialer.Open(PhoneNumber);
}
catch (Exception ex)
{
await DisplayAlertAsync($"Dialer is not supported: {ex.Message}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class PreferencesViewModel : BaseViewModel
{
const string preferenceKey = "PreferenceKey";
string preferenceValue;
public PreferencesViewModel()
{
preferenceValue = Preferences.Get(preferenceKey, string.Empty);
}
public string PreferenceValue
{
get => preferenceValue;
set
{
preferenceValue = value;
Preferences.Set(preferenceKey, value);
OnPropertyChanged();
}
}
}
}

Просмотреть файл

@ -0,0 +1,51 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
class ScreenshotViewModel : BaseViewModel
{
ImageSource screenshot;
public ScreenshotViewModel()
{
ScreenshotCommand = new Command(async () => await CaptureScreenshot(), () => Screenshot.IsCaptureSupported);
EmailCommand = new Command(async () => await EmailScreenshot(), () => Screenshot.IsCaptureSupported);
}
public ICommand ScreenshotCommand { get; }
public ICommand EmailCommand { get; }
public ImageSource Image
{
get => screenshot;
set => SetProperty(ref screenshot, value);
}
async Task CaptureScreenshot()
{
var mediaFile = await Screenshot.CaptureAsync();
var stream = await mediaFile.OpenReadAsync(ScreenshotFormat.Png);
Image = ImageSource.FromStream(() => stream);
}
async Task EmailScreenshot()
{
var mediaFile = await Screenshot.CaptureAsync();
var temp = Path.Combine(FileSystem.CacheDirectory, "screenshot.jpg");
using (var stream = await mediaFile.OpenReadAsync(ScreenshotFormat.Jpeg))
using (var file = File.Create(temp))
{
await stream.CopyToAsync(file);
}
await Email.ComposeAsync(new EmailMessage
{
Attachments = { new EmailAttachment(temp) }
});
}
}
}

Просмотреть файл

@ -0,0 +1,108 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class SecureStorageViewModel : BaseViewModel
{
string key;
string securedValue;
public SecureStorageViewModel()
{
LoadCommand = new Command(OnLoad);
SaveCommand = new Command(OnSave);
RemoveCommand = new Command(OnRemove);
RemoveAllCommand = new Command(OnRemoveAll);
}
public string Key
{
get => key;
set => SetProperty(ref key, value);
}
public string SecuredValue
{
get => securedValue;
set => SetProperty(ref securedValue, value);
}
public ICommand LoadCommand { get; }
public ICommand SaveCommand { get; }
public ICommand RemoveCommand { get; }
public ICommand RemoveAllCommand { get; }
async void OnLoad()
{
if (IsBusy)
return;
IsBusy = true;
try
{
SecuredValue = await SecureStorage.GetAsync(Key) ?? string.Empty;
}
catch (Exception ex)
{
await DisplayAlertAsync(ex.Message);
}
IsBusy = false;
}
async void OnSave()
{
if (IsBusy)
return;
IsBusy = true;
try
{
await SecureStorage.SetAsync(Key, SecuredValue);
}
catch (Exception ex)
{
await DisplayAlertAsync(ex.Message);
}
IsBusy = false;
}
async void OnRemove()
{
if (IsBusy)
return;
IsBusy = true;
try
{
SecureStorage.Remove(Key);
}
catch (Exception ex)
{
await DisplayAlertAsync(ex.Message);
}
IsBusy = false;
}
async void OnRemoveAll()
{
if (IsBusy)
return;
IsBusy = true;
try
{
SecureStorage.RemoveAll();
}
catch (Exception ex)
{
await DisplayAlertAsync(ex.Message);
}
IsBusy = false;
}
}
}

Просмотреть файл

@ -0,0 +1,171 @@
using PlatformIntegrationDemo.Helpers;
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
class ShareViewModel : BaseViewModel
{
bool shareText = true;
bool shareUri;
string text;
string uri;
string subject;
string title;
string shareFileAttachmentContents;
string shareFileAttachmentName;
string shareFileTitle;
string shareFilesTitle;
string shareFile1AttachmentContents;
string shareFile1AttachmentName;
string shareFile2AttachmentContents;
string shareFile2AttachmentName;
public ICommand RequestCommand { get; }
public ICommand RequestFileCommand { get; }
public ICommand RequestFilesCommand { get; }
public ShareViewModel()
{
RequestCommand = new Command<Microsoft.Maui.Controls.View>(OnRequest);
RequestFileCommand = new Command<Microsoft.Maui.Controls.View>(OnFileRequest);
RequestFilesCommand = new Command<Microsoft.Maui.Controls.View>(OnFilesRequest);
}
public bool ShareText
{
get => shareText;
set => SetProperty(ref shareText, value);
}
public bool ShareUri
{
get => shareUri;
set => SetProperty(ref shareUri, value);
}
public string Text
{
get => text;
set => SetProperty(ref text, value);
}
public string Uri
{
get => uri;
set => SetProperty(ref uri, value);
}
public string Subject
{
get => subject;
set => SetProperty(ref subject, value);
}
public string Title
{
get => title;
set => SetProperty(ref title, value);
}
public string ShareFileTitle
{
get => shareFileTitle;
set => SetProperty(ref shareFileTitle, value);
}
public string ShareFileAttachmentContents
{
get => shareFileAttachmentContents;
set => SetProperty(ref shareFileAttachmentContents, value);
}
public string ShareFileAttachmentName
{
get => shareFileAttachmentName;
set => SetProperty(ref shareFileAttachmentName, value);
}
public string ShareFilesTitle
{
get => shareFilesTitle;
set => SetProperty(ref shareFilesTitle, value);
}
public string ShareFile1AttachmentContents
{
get => shareFile1AttachmentContents;
set => SetProperty(ref shareFile1AttachmentContents, value);
}
public string ShareFile1AttachmentName
{
get => shareFile1AttachmentName;
set => SetProperty(ref shareFile1AttachmentName, value);
}
public string ShareFile2AttachmentContents
{
get => shareFile2AttachmentContents;
set => SetProperty(ref shareFile2AttachmentContents, value);
}
public string ShareFile2AttachmentName
{
get => shareFile2AttachmentName;
set => SetProperty(ref shareFile2AttachmentName, value);
}
async void OnRequest(Microsoft.Maui.Controls.View element)
=> await Share.RequestAsync(new ShareTextRequest
{
Subject = Subject,
Text = ShareText ? Text : null,
Uri = ShareUri ? Uri : null,
Title = Title,
PresentationSourceBounds = element.GetAbsoluteBounds()
});
async void OnFileRequest(Microsoft.Maui.Controls.View element)
{
if (string.IsNullOrWhiteSpace(ShareFileAttachmentContents))
return;
var file = CreateFile(ShareFileAttachmentName, ShareFileAttachmentContents, "Attachment.txt");
await Share.RequestAsync(new ShareFileRequest
{
Title = ShareFileTitle,
File = new ShareFile(file),
PresentationSourceBounds = element.GetAbsoluteBounds()
});
}
async void OnFilesRequest(Microsoft.Maui.Controls.View element)
{
if (string.IsNullOrWhiteSpace(ShareFile1AttachmentContents) ||
string.IsNullOrWhiteSpace(ShareFile2AttachmentContents))
return;
var file1 = CreateFile(ShareFile1AttachmentName, ShareFile1AttachmentContents, "Attachment1.txt");
var file2 = CreateFile(ShareFile2AttachmentName, ShareFile2AttachmentContents, "Attachment2.txt");
await Share.RequestAsync(new ShareMultipleFilesRequest
{
Title = ShareFilesTitle,
Files = new List<ShareFile> { new ShareFile(file1), new ShareFile(file2) },
PresentationSourceBounds = element.GetAbsoluteBounds()
});
}
string CreateFile(string fileName, string fileContents, string emptyName)
{
var fn = string.IsNullOrWhiteSpace(fileName) ? emptyName : fileName.Trim();
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, fileContents);
return file;
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class SmsViewModel : BaseViewModel
{
string recipient;
string messageText;
public SmsViewModel()
{
SendSmsCommand = new Command(OnSendSms);
}
public string Recipient
{
get => recipient;
set => SetProperty(ref recipient, value);
}
public string MessageText
{
get => messageText;
set => SetProperty(ref messageText, value);
}
public ICommand SendSmsCommand { get; }
async void OnSendSms()
{
if (IsBusy)
return;
IsBusy = true;
try
{
var message = new SmsMessage(MessageText, Recipient.Split(',', '*'));
await Sms.ComposeAsync(message);
}
catch (FeatureNotSupportedException)
{
await DisplayAlertAsync("Sending an SMS is not supported on this device.");
}
catch (Exception ex)
{
await DisplayAlertAsync($"Unable to send Sms: {ex.Message}");
}
IsBusy = false;
}
}
}

Просмотреть файл

@ -0,0 +1,145 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class TextToSpeechViewModel : BaseViewModel
{
CancellationTokenSource cts;
string text;
bool advancedOptions;
float volume;
float pitch;
string locale = "Default";
Locale selectedLocale;
public TextToSpeechViewModel()
{
SpeakCommand = new Command<bool>(OnSpeak);
CancelCommand = new Command(OnCancel);
PickLocaleCommand = new Command(async () => await OnPickLocale());
Text = ".NET MAUI platform integration functionality makes text to speech easy!";
AdvancedOptions = false;
Volume = 1.0f;
Pitch = 1.0f;
}
public override void OnDisappearing()
{
OnCancel();
base.OnDisappearing();
}
void OnSpeak(bool multiple)
{
if (IsBusy)
return;
IsBusy = true;
cts = new CancellationTokenSource();
SpeechOptions options = null;
if (AdvancedOptions)
{
options = new SpeechOptions
{
Volume = Volume,
Pitch = Pitch,
Locale = selectedLocale
};
}
Task speaks = null;
if (multiple)
{
speaks = Task.WhenAll(
TextToSpeech.SpeakAsync(Text + " 1 ", options, cancelToken: cts.Token),
TextToSpeech.SpeakAsync(Text + " 2 ", options, cancelToken: cts.Token),
TextToSpeech.SpeakAsync(Text + " 3 ", options, cancelToken: cts.Token));
}
else
{
speaks = TextToSpeech.SpeakAsync(Text, options, cts.Token);
}
// use ContinueWith so we don't have to catch the cancelled exceptions
speaks.ContinueWith(t => IsBusy = false);
}
void OnCancel()
{
if (!IsBusy && (cts?.IsCancellationRequested ?? true))
return;
cts.Cancel();
IsBusy = false;
}
async Task OnPickLocale()
{
var allLocales = await TextToSpeech.GetLocalesAsync();
var locales = allLocales
.OrderBy(i => i.Language.ToLowerInvariant())
.ToArray();
var languages = locales
.Select(i => string.IsNullOrEmpty(i.Country) ? i.Language : $"{i.Language} ({i.Country})")
.ToArray();
var result = await Application.Current.MainPage.DisplayActionSheet("Pick", "OK", null, languages);
if (!string.IsNullOrEmpty(result) && Array.IndexOf(languages, result) is int idx && idx != -1)
{
selectedLocale = locales[idx];
Locale = result;
}
else
{
selectedLocale = null;
Locale = "Default";
}
}
public ICommand CancelCommand { get; }
public ICommand SpeakCommand { get; }
public ICommand PickLocaleCommand { get; }
public string Text
{
get => text;
set => SetProperty(ref text, value);
}
public bool AdvancedOptions
{
get => advancedOptions;
set => SetProperty(ref advancedOptions, value);
}
public float Volume
{
get => volume;
set => SetProperty(ref volume, value);
}
public float Pitch
{
get => pitch;
set => SetProperty(ref pitch, value);
}
public string Locale
{
get => locale;
set => SetProperty(ref locale, value);
}
}
}

Просмотреть файл

@ -0,0 +1,39 @@
namespace PlatformIntegrationDemo.ViewModels
{
public class UnitConvertersViewModel : BaseViewModel
{
double fahrenheit;
double celsius;
double miles;
double kilometers;
public UnitConvertersViewModel()
{
}
public double Fahrenheit
{
get => fahrenheit;
set => SetProperty(ref fahrenheit, value, onChanged: () => Celsius = UnitConverters.FahrenheitToCelsius(fahrenheit));
}
public double Celsius
{
get => celsius;
set => SetProperty(ref celsius, value);
}
public double Miles
{
get => miles;
set => SetProperty(ref miles, value, onChanged: () => Kilometers = UnitConverters.MilesToKilometers(miles));
}
public double Kilometers
{
get => kilometers;
set => SetProperty(ref kilometers, value);
}
}
}

Просмотреть файл

@ -0,0 +1,72 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class VibrationViewModel : BaseViewModel
{
int duration = 500;
bool isSupported = true;
public VibrationViewModel()
{
VibrateCommand = new Command(OnVibrate);
CancelCommand = new Command(OnCancel);
}
public ICommand VibrateCommand { get; }
public ICommand CancelCommand { get; }
public int Duration
{
get => duration;
set => SetProperty(ref duration, value);
}
public bool IsSupported
{
get => isSupported;
set => SetProperty(ref isSupported, value);
}
public override void OnDisappearing()
{
OnCancel();
base.OnDisappearing();
}
void OnVibrate()
{
try
{
Vibration.Vibrate(duration);
}
catch (FeatureNotSupportedException)
{
IsSupported = false;
}
catch (Exception ex)
{
DisplayAlertAsync($"Unable to vibrate: {ex.Message}");
}
}
void OnCancel()
{
try
{
Vibration.Cancel();
}
catch (FeatureNotSupportedException)
{
IsSupported = false;
}
catch (Exception ex)
{
DisplayAlertAsync($"Unable to cancel: {ex.Message}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,84 @@
using System.Windows.Input;
namespace PlatformIntegrationDemo.ViewModels
{
public class WebAuthenticatorViewModel : BaseViewModel
{
const string authenticationUrl = "https://xamarin-essentials-auth-sample.azurewebsites.net/mobileauth/";
public WebAuthenticatorViewModel()
{
MicrosoftCommand = new Command(async () => await OnAuthenticate("Microsoft"));
GoogleCommand = new Command(async () => await OnAuthenticate("Google"));
FacebookCommand = new Command(async () => await OnAuthenticate("Facebook"));
AppleCommand = new Command(async () => await OnAuthenticate("Apple"));
}
public ICommand MicrosoftCommand { get; }
public ICommand GoogleCommand { get; }
public ICommand FacebookCommand { get; }
public ICommand AppleCommand { get; }
string accessToken = string.Empty;
public string AuthToken
{
get => accessToken;
set => SetProperty(ref accessToken, value);
}
async Task OnAuthenticate(string scheme)
{
try
{
WebAuthenticatorResult r = null;
if (scheme.Equals("Apple", StringComparison.Ordinal)
&& DeviceInfo.Platform == DevicePlatform.iOS
&& DeviceInfo.Version.Major >= 13)
{
// Make sure to enable Apple Sign In in both the
// entitlements and the provisioning profile.
var options = new AppleSignInAuthenticator.Options
{
IncludeEmailScope = true,
IncludeFullNameScope = true,
};
r = await AppleSignInAuthenticator.AuthenticateAsync(options);
}
else
{
var authUrl = new Uri(authenticationUrl + scheme);
var callbackUrl = new Uri("xamarinessentials://");
r = await WebAuthenticator.AuthenticateAsync(authUrl, callbackUrl);
}
AuthToken = string.Empty;
if (r.Properties.TryGetValue("name", out var name) && !string.IsNullOrEmpty(name))
AuthToken += $"Name: {name}{Environment.NewLine}";
if (r.Properties.TryGetValue("email", out var email) && !string.IsNullOrEmpty(email))
AuthToken += $"Email: {email}{Environment.NewLine}";
AuthToken += r?.AccessToken ?? r?.IdToken;
}
catch (OperationCanceledException)
{
Console.WriteLine("Login canceled.");
AuthToken = string.Empty;
await DisplayAlertAsync("Login canceled.");
}
catch (Exception ex)
{
Console.WriteLine($"Failed: {ex.Message}");
AuthToken = string.Empty;
await DisplayAlertAsync($"Failed: {ex.Message}");
}
}
}
}

Просмотреть файл

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.AccelerometerPage"
Title="Accelerometer">
<views:BasePage.BindingContext>
<viewmodels:AccelerometerViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Retrieve acceleration data of the device in 3D space. Additionally, this page will detect if you shake your device."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<Grid Padding="12,0,12,12"
ColumnDefinitions="*,*"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding X, StringFormat='X: {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="{Binding Y, StringFormat='Y: {0:N}'}" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding Z, StringFormat='Z: {0:N}'}" />
<Picker Grid.Row="4"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
<Label Grid.Row="6"
Grid.ColumnSpan="2"
Text="{Binding ShakeTime}" />
</Grid>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class AccelerometerPage : BasePage
{
public AccelerometerPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,280 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.AllSensorsPage"
Title="All Sensors">
<Grid RowDefinitions="Auto,*">
<Label Text="Have a look at the accelerometer, compass, gyroscope and magnetometer."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<StackLayout Padding="0,0,0,12"
Spacing="6">
<Label Text="Accelerometer"
FontAttributes="Bold"
Margin="12" />
<Grid x:Name="GridAccelerometer">
<Grid.BindingContext>
<viewmodels:AccelerometerViewModel />
</Grid.BindingContext>
<Grid Padding="12,0,12,12"
ColumnDefinitions="*,*"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding X, StringFormat='X: {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="{Binding Y, StringFormat='Y: {0:N}'}" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding Z, StringFormat='Z: {0:N}'}" />
<Picker Grid.Row="4"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
<Label Grid.Row="6"
Grid.ColumnSpan="2"
Text="{Binding ShakeTime}" />
</Grid>
</Grid>
<Label Text="Barometer"
FontAttributes="Bold"
Margin="12" />
<Grid x:Name="GridBarometer">
<Grid.BindingContext>
<viewmodels:BarometerViewModel />
</Grid.BindingContext>
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding Pressure, StringFormat='Barometer pressure (hPA): {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="Speed:" />
<Picker Grid.Row="2"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}"
Margin="0,0,0,10" />
<Button Grid.Row="3"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="3"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</Grid>
<Label Text="Compass"
FontAttributes="Bold"
Margin="12" />
<Grid x:Name="GridCompass">
<Grid.BindingContext>
<viewmodels:CompassViewModel />
</Grid.BindingContext>
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding Heading, StringFormat='Heading (degrees): {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="Speed:" />
<Picker Grid.Row="2"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}"
Margin="0,0,0,10" />
<Label Grid.Row="3"
Grid.ColumnSpan="2"
Text="Apply low pass filter:" />
<Switch Grid.Row="4"
Grid.ColumnSpan="2"
IsToggled="{Binding ApplyLowPassFilter}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</Grid>
<Label Text="Gyroscope"
FontAttributes="Bold"
Margin="12" />
<Grid x:Name="GridGyro">
<Grid.BindingContext>
<viewmodels:GyroscopeViewModel />
</Grid.BindingContext>
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding X, StringFormat='X: {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="{Binding Y, StringFormat='Y: {0:N}'}" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding Z, StringFormat='Z: {0:N}'}" />
<Picker Grid.Row="4"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</Grid>
<Label Text="Magnetometer"
FontAttributes="Bold"
Margin="12" />
<Grid x:Name="GridMagnetometer">
<Grid.BindingContext>
<viewmodels:MagnetometerViewModel />
</Grid.BindingContext>
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding X, StringFormat='X: {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="{Binding Y, StringFormat='Y: {0:N}'}" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding Z, StringFormat='Z: {0:N}'}" />
<Picker Grid.Row="4"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</Grid>
<Label Text="Orientation Sensor"
FontAttributes="Bold"
Margin="12" />
<Grid x:Name="GridOrientation">
<Grid.BindingContext>
<viewmodels:OrientationSensorViewModel />
</Grid.BindingContext>
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding X, StringFormat='X: {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="{Binding Y, StringFormat='Y: {0:N}'}" />
<Label Grid.Row="2"
Grid.ColumnSpan="2"
Text="{Binding Z, StringFormat='Z: {0:N}'}" />
<Label Grid.Row="3"
Grid.ColumnSpan="2"
Text="{Binding W, StringFormat='W: {0:N}'}" />
<Picker Grid.Row="4"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</Grid>
</StackLayout>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,33 @@
namespace PlatformIntegrationDemo.Views;
public partial class AllSensorsPage : BasePage
{
public AllSensorsPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
SetupBinding(GridAccelerometer.BindingContext);
SetupBinding(GridCompass.BindingContext);
SetupBinding(GridGyro.BindingContext);
SetupBinding(GridMagnetometer.BindingContext);
SetupBinding(GridOrientation.BindingContext);
SetupBinding(GridBarometer.BindingContext);
}
protected override void OnDisappearing()
{
TearDownBinding(GridAccelerometer.BindingContext);
TearDownBinding(GridCompass.BindingContext);
TearDownBinding(GridGyro.BindingContext);
TearDownBinding(GridMagnetometer.BindingContext);
TearDownBinding(GridOrientation.BindingContext);
TearDownBinding(GridBarometer.BindingContext);
base.OnDisappearing();
}
}

Просмотреть файл

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.AppInfoPage"
Title="App Info">
<views:BasePage.BindingContext>
<viewmodels:AppInfoViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Find out about the app with ease."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<StackLayout Padding="12,0,12,12"
Spacing="6">
<Label Text="App Info:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Label Text="{Binding AppName, StringFormat='Name: {0}'}" />
<Label Text="{Binding AppPackageName, StringFormat='Package Name: {0}'}" />
<Label Text="{Binding AppTheme, StringFormat='Theme: {0}'}" />
<Label Text="Version Info:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Label Text="{Binding AppVersion, StringFormat='Version: {0}'}" />
<Label Text="{Binding AppBuild, StringFormat='Build: {0}'}" />
<Label Text="{Binding RequestedLayoutDirection, StringFormat='Layout Direction: {0}'}" />
<Button Text="Open Settings"
Command="{Binding ShowSettingsUICommand}" />
</StackLayout>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class AppInfoPage : BasePage
{
public AppInfoPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.BarometerPage"
Title="Barometer">
<views:BasePage.BindingContext>
<viewmodels:BarometerViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Monitor barometer for changes."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding Pressure, StringFormat='Barometer pressure (hPA): {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="Speed:" />
<Picker Grid.Row="2"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}"
Margin="0,0,0,10" />
<Button Grid.Row="3"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="3"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class BarometerPage : BasePage
{
public BarometerPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,65 @@
using PlatformIntegrationDemo.ViewModels;
namespace PlatformIntegrationDemo.Views;
public class BasePage : ContentPage
{
public BasePage()
{
NavigationPage.SetBackButtonTitle(this, "Back");
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
void OnLoaded(object sender, EventArgs e)
{
SetupBinding(BindingContext);
}
void OnUnloaded(object sender, EventArgs e)
{
TearDownBinding(BindingContext);
}
protected void SetupBinding(object bindingContext)
{
if (bindingContext is BaseViewModel vm)
{
vm.DoDisplayAlert += OnDisplayAlert;
vm.DoNavigate += OnNavigate;
vm.OnAppearing();
}
}
protected void TearDownBinding(object bindingContext)
{
if (bindingContext is BaseViewModel vm)
{
vm.OnDisappearing();
vm.DoDisplayAlert -= OnDisplayAlert;
vm.DoNavigate -= OnNavigate;
}
}
Task OnDisplayAlert(string message)
{
return DisplayAlert(Title, message, "OK");
}
Task OnNavigate(BaseViewModel vm, bool showModal)
{
var name = vm.GetType().Name;
name = name.Replace("ViewModel", "Page", StringComparison.Ordinal);
var ns = GetType().Namespace;
var pageType = Type.GetType($"{ns}.{name}");
var page = (BasePage)Activator.CreateInstance(pageType);
page.BindingContext = vm;
return showModal
? Navigation.PushModalAsync(page)
: Navigation.PushAsync(page);
}
}

Просмотреть файл

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.BatteryPage"
Title="Battery">
<views:BasePage.BindingContext>
<viewmodels:BatteryViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Easily detect battery level, source, and state."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<StackLayout Padding="12,0,12,12"
Spacing="6">
<Label Text="Battery State:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Label Text="{Binding Level, StringFormat='Charge Level: {0:P1}'}" />
<Label Text="{Binding State, StringFormat='State: {0}'}" />
<Label Text="{Binding PowerSource, StringFormat='Power Source: {0}'}" />
<Label Text="{Binding EnergySaverStatus, StringFormat='Energy Saver: {0}'}" />
</StackLayout>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class BatteryPage : BasePage
{
public BatteryPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.BrowserPage"
Title="Browser">
<views:BasePage.BindingContext>
<viewmodels:BrowserViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Quickly and easily open URIs."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<StackLayout Padding="12,0,12,12"
Spacing="6">
<Label Text="URI:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Label Text="Enter URI:" />
<Entry Placeholder="https://..."
Text="{Binding Uri}" />
<Button Text="Open URI"
Command="{Binding OpenUriCommand}"
IsEnabled="{Binding IsNotBusy}" />
<Label Text="Launch Type:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Picker HorizontalOptions="FillAndExpand"
ItemsSource="{Binding BrowserLaunchModes}"
SelectedIndex="{Binding BrowserType, Mode=TwoWay}" />
<Label Text="Launch Options:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Label Text="Title Mode:" />
<Picker HorizontalOptions="FillAndExpand"
ItemsSource="{Binding BrowserTitleModes}"
SelectedIndex="{Binding BrowserTitleType, Mode=TwoWay}" />
<Label Text="Toolbar Color:" />
<Picker HorizontalOptions="FillAndExpand"
ItemsSource="{Binding AllColors}"
SelectedIndex="{Binding ToolbarColor, Mode=TwoWay}" />
<Label Text="Control Tint Color (iOS):" />
<Picker HorizontalOptions="FillAndExpand"
ItemsSource="{Binding AllColors}"
SelectedIndex="{Binding ControlColor, Mode=TwoWay}" />
<Label Text="Flags:"
FontAttributes="Bold"
Margin="0,6,0,0" />
<Grid ColumnDefinitions="*,Auto"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<Label Text="Launch Adjacent (Android)"
Grid.Column="0"
Grid.Row="0" />
<Switch IsToggled="{Binding LaunchAdjacent, Mode=TwoWay}"
Grid.Column="1"
Grid.Row="0" />
<Label Text="Present as Form Sheet (iOS)"
Grid.Column="0"
Grid.Row="1" />
<Switch IsToggled="{Binding PresentAsFormSheet, Mode=TwoWay}"
Grid.Column="1"
Grid.Row="1" />
<Label Text="Present as Page Sheet (iOS)"
Grid.Column="0"
Grid.Row="2" />
<Switch IsToggled="{Binding PresentAsPageSheet, Mode=TwoWay}"
Grid.Column="1"
Grid.Row="2" />
</Grid>
<Label Text="{Binding BrowserStatus}" />
</StackLayout>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class BrowserPage : BasePage
{
public BrowserPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.ClipboardPage"
Title="Clipboard">
<views:BasePage.BindingContext>
<viewmodels:ClipboardViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Quickly and easily use the clipboard."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<StackLayout Padding="12,0,12,12"
Spacing="6">
<Label Text="Enter some text:" />
<Entry Placeholder="Enter text..."
Text="{Binding FieldValue}" />
<Button Text="Copy to clipboard"
Command="{Binding CopyCommand}" />
<Button Text="Paste from clipboard"
Command="{Binding PasteCommand}" />
<Button Text="Check Status"
Command="{Binding CheckCommand}" />
<Label Text="{Binding LastCopied}" />
</StackLayout>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class ClipboardPage : BasePage
{
public ClipboardPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.ColorConvertersPage"
Title="Color Converters">
<views:BasePage.BindingContext>
<viewmodels:ColorConvertersViewModel />
</views:BasePage.BindingContext>
<views:BasePage.Resources>
<ResourceDictionary>
<Style TargetType="BoxView">
<Setter Property="CornerRadius"
Value="8" />
<Setter Property="HeightRequest"
Value="24" />
<Setter Property="WidthRequest"
Value="100" />
<Setter Property="VerticalOptions"
Value="Center" />
</Style>
<Style TargetType="Slider">
<Setter Property="VerticalOptions"
Value="Center" />
</Style>
</ResourceDictionary>
</views:BasePage.Resources>
<Grid RowDefinitions="Auto,*">
<Label Text="Create, convert, and adjust colors."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<Grid Padding="12,0,12,12"
RowSpacing="6"
ColumnSpacing="6"
ColumnDefinitions="*,Auto"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<Label Text="Hex Color:"
Grid.Row="0"
Grid.ColumnSpan="2" />
<Entry Text="{Binding Hex}"
HorizontalOptions="Fill"
Grid.Row="1"
Grid.Column="0" />
<BoxView Color="{Binding RegularColor}"
Grid.Row="1"
Grid.Column="1" />
<Label Text="Complementary Color:"
Grid.Row="2"
Grid.ColumnSpan="2" />
<Label Text="{Binding ComplementHex}"
HorizontalOptions="Fill"
Grid.Row="3"
Grid.Column="0" />
<BoxView Color="{Binding ComplementColor}"
Grid.Row="3"
Grid.Column="1" />
<Label Text="{Binding Alpha, StringFormat='Alpha: {0:F0}'}"
Grid.Row="4"
Grid.ColumnSpan="2" />
<Slider Minimum="0"
Maximum="255"
Value="{Binding Alpha}"
Grid.Row="5"
Grid.Column="0" />
<BoxView Color="{Binding AlphaColor}"
Grid.Row="5"
Grid.Column="1" />
<Label Text="{Binding Hue, StringFormat='Hue: {0:F0}'}"
Grid.Row="6"
Grid.ColumnSpan="2" />
<Slider Minimum="0"
Maximum="360"
Value="{Binding Hue}"
Grid.Row="7"
Grid.Column="0" />
<BoxView Color="{Binding HueColor}"
Grid.Row="7"
Grid.Column="1" />
<Label Text="{Binding Luminosity, StringFormat='Luminosity: {0:F0}'}"
Grid.Row="8"
Grid.ColumnSpan="2" />
<Slider Minimum="0"
Maximum="100"
Value="{Binding Luminosity}"
Grid.Row="9"
Grid.Column="0" />
<BoxView Color="{Binding LuminosityColor}"
Grid.Row="9"
Grid.Column="1" />
<Label Text="{Binding Saturation, StringFormat='Saturation: {0:F0}'}"
Grid.Row="10"
Grid.ColumnSpan="2" />
<Slider Minimum="0"
Maximum="100"
Value="{Binding Saturation}"
Grid.Row="11"
Grid.Column="0" />
<BoxView Color="{Binding SaturationColor}"
Grid.Row="11"
Grid.Column="1" />
</Grid>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class ColorConvertersPage : BasePage
{
public ColorConvertersPage()
{
InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:PlatformIntegrationDemo.Views"
xmlns:viewmodels="clr-namespace:PlatformIntegrationDemo.ViewModels"
x:Class="PlatformIntegrationDemo.Views.CompassPage"
Title="Compass">
<views:BasePage.BindingContext>
<viewmodels:CompassViewModel />
</views:BasePage.BindingContext>
<Grid RowDefinitions="Auto,*">
<Label Text="Monitor compass for changes."
FontAttributes="Bold"
Margin="12" />
<ScrollView Grid.Row="1">
<Grid Padding="12,0,12,12"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"
ColumnDefinitions="*,*">
<Label Grid.Row="0"
Grid.ColumnSpan="2"
Text="{Binding Heading, StringFormat='Heading (degrees): {0:N}'}" />
<Label Grid.Row="1"
Grid.ColumnSpan="2"
Text="Speed:" />
<Picker Grid.Row="2"
Grid.ColumnSpan="2"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Speeds}"
SelectedIndex="{Binding Speed, Mode=TwoWay}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}"
Margin="0,0,0,10" />
<Label Grid.Row="3"
Grid.ColumnSpan="2"
Text="Apply low pass filter:" />
<Switch Grid.Row="4"
Grid.ColumnSpan="2"
IsToggled="{Binding ApplyLowPassFilter}" />
<Button Grid.Row="5"
Grid.Column="0"
Text="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding IsActive, Converter={StaticResource InverterConverter}}" />
<Button Grid.Row="5"
Grid.Column="1"
Text="Stop"
Command="{Binding StopCommand}"
IsEnabled="{Binding IsActive}" />
</Grid>
</ScrollView>
</Grid>
</views:BasePage>

Просмотреть файл

@ -0,0 +1,9 @@
namespace PlatformIntegrationDemo.Views;
public partial class CompassPage : BasePage
{
public CompassPage()
{
InitializeComponent();
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше