Port Camelotia to Android (Xamarin.Forms) (#9)

* Update packages, add XF + Android projects
* Reference proper packages, implement main view
* Implement MasterDetail pattern
* Implement Provider view
* Add authentication pages
* Fix login-logout issues
* Pull2refresh, use spaces instead of tabs
* Hide unrelated auth pages
* Improved GUI and styles
* Update README.md
* Fix extensions
* Wider images
* Align images properly
* Images were too big
* Better vendor descriptions
* Build only Avalonia project now
* Cleanup
* GUI tweaks
* Implement file picker
* Implement file upload & download
* Fix first provider won't get selected
* Implement authentication service
This commit is contained in:
Artyom 2018-12-31 19:10:45 +03:00 коммит произвёл GitHub
Родитель 51b57574e0
Коммит 9c3f15caae
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
56 изменённых файлов: 8667 добавлений и 19 удалений

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

@ -5,16 +5,19 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="2.3.2">
<PackageReference Include="coverlet.msbuild" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="5.5.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="ReactiveUI.Testing" Version="9.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="ReactiveUI.Testing" Version="9.5.1" />
<PackageReference Include="NSubstitute" Version="3.1.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

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

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{9AA2D3C6-3393-45F1-8E7C-5A9901728795}</TemplateGuid>
<OutputType>Library</OutputType>
<RootNamespace>Camelotia.Presentation.Xamarin.Droid</RootNamespace>
<AssemblyName>Camelotia.Presentation.Xamarin.Android</AssemblyName>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<BundleAssemblies>false</BundleAssemblies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidManagedSymbols>true</AndroidManagedSymbols>
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Reactive" Version="4.1.2" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.2" />
<PackageReference Include="ReactiveUI.AndroidSupport">
<Version>9.5.1</Version>
</PackageReference>
<PackageReference Include="Xamarin.Forms" Version="3.4.0.1008975" />
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0" />
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="28.0.0" />
<PackageReference Include="Xamarin.Android.Support.v4" Version="28.0.0" />
<PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="28.0.0" />
<PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="28.0.0" />
<PackageReference Include="Xamarin.Plugin.FilePicker">
<Version>2.0.135</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Threading.Tasks.Extensions">
<HintPath>$(UserProfile)\.nuget\packages\system.threading.tasks.extensions\4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\AndroidAuthenticator.cs" />
<Compile Include="Services\AndroidFileManager.cs" />
<Compile Include="WebActivity.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Assets\AboutAssets.txt" />
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" />
<AndroidResource Include="Resources\values\styles.xml" />
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\icon.xml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\icon_round.xml" />
<AndroidResource Include="Resources\mipmap-hdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-hdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-mdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-mdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\launcher_foreground.png" />
<AndroidResource Include="Resources\drawable\xamarin_logo.png" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\drawable-hdpi\" />
<Folder Include="Resources\drawable-xhdpi\" />
<Folder Include="Resources\drawable-xxhdpi\" />
<Folder Include="Resources\drawable-xxxhdpi\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Camelotia.Presentation.Xamarin\Camelotia.Presentation.Xamarin.csproj">
<Project>{4c429bb0-c9d8-43d0-bac3-d5dd93e89d65}</Project>
<Name>Camelotia.Presentation.Xamarin</Name>
</ProjectReference>
<ProjectReference Include="..\Camelotia.Presentation\Camelotia.Presentation.csproj">
<Project>{777FBFA6-ECFA-42F5-AC5F-EFC4B2D4AE6B}</Project>
<Name>Camelotia.Presentation</Name>
</ProjectReference>
<ProjectReference Include="..\Camelotia.Services\Camelotia.Services.csproj">
<Project>{283B8F56-AB9F-4556-AB93-AAC54171841C}</Project>
<Name>Camelotia.Services</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\WebActivity.axml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

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

@ -0,0 +1,73 @@
using Camelotia.Presentation.Interfaces;
using Camelotia.Presentation.ViewModels;
using Camelotia.Presentation.Xamarin.Droid.Services;
using Camelotia.Services.Providers;
using Camelotia.Services.Storages;
using System.Reactive.Concurrency;
using System.Reactive.Subjects;
using System;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Content;
using ReactiveUI;
namespace Camelotia.Presentation.Xamarin.Droid
{
[Activity(
Label = "Camelotia.Presentation.Xamarin",
Icon = "@mipmap/icon",
Theme = "@style/MainTheme",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private readonly ISubject<string> _authenticationCodeReceived = new Subject<string>();
public IObservable<string> AuthenticationCodeReceived => _authenticationCodeReceived;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App(BuildMainViewModel()));
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode != 42 || resultCode != Result.Ok) return;
var code = data.GetStringExtra("token");
_authenticationCodeReceived.OnNext(code);
}
private IMainViewModel BuildMainViewModel()
{
var currentThread = CurrentThreadScheduler.Instance;
var mainThread = RxApp.MainThreadScheduler;
var cache = new AkavacheTokenStorage();
return new MainViewModel(
(provider, files, auth) => new ProviderViewModel(auth, files, currentThread, mainThread, provider),
provider => new AuthViewModel(
new DirectAuthViewModel(currentThread, mainThread, provider),
new OAuthViewModel(currentThread, mainThread, provider),
currentThread,
provider
),
new ProviderStorage(
new VkontakteFileSystemProvider(cache),
new YandexFileSystemProvider(
new AndroidAuthenticator(this), cache
)
),
new AndroidFileManager(this),
currentThread,
mainThread
);
}
}
}

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
<application android:label="Camelotia.Presentation.Xamarin.Android"></application>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

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

@ -0,0 +1,20 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
[assembly: AssemblyTitle("Camelotia.Presentation.Xamarin.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Camelotia.Presentation.Xamarin.Android")]
[assembly: AssemblyCopyright("Copyright © 2014")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]

7516
Camelotia.Presentation.Xamarin.Android/Resources/Resource.designer.cs сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:tabIndicatorColor="@android:color/white"
app:tabGravity="fill"
app:tabMode="fixed" />

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

@ -0,0 +1,9 @@
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

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

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/web_activity_web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

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

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

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

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

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

После

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

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

После

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

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

После

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

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

После

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

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

После

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

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

После

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

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

После

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

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

После

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

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

После

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

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

После

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

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

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="launcher_background">#000063</color>
<color name="colorPrimary">#311b92</color>
<color name="colorPrimaryDark">#000063</color>
<color name="colorAccent">#6746c3</color>
<color name="ListViewSelected">#c5cae9</color>
<color name="ListViewHighlighted">#c5cae9</color>
</resources>

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

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<style name="MainTheme" parent="MainTheme.Base"></style>
<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="colorPrimary">#311b92</item>
<item name="colorPrimaryDark">#000063</item>
<item name="colorAccent">#6746c3</item>
<item name="windowActionModeOverlay">true</item>
<item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
<item name="android:colorPressedHighlight">@color/ListViewSelected</item>
<item name="android:colorLongPressedHighlight">@color/ListViewHighlighted</item>
<item name="android:colorFocusedHighlight">@color/ListViewSelected</item>
<item name="android:colorActivatedHighlight">@color/ListViewSelected</item>
<item name="android:activatedBackgroundIndicator">@color/ListViewSelected</item>
</style>
<style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">#6746c3</item>
</style>
</resources>

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

@ -0,0 +1,35 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Android.Content;
using Camelotia.Services.Interfaces;
namespace Camelotia.Presentation.Xamarin.Droid.Services
{
public sealed class AndroidAuthenticator : IAuthenticator
{
private readonly MainActivity _activity;
public AndroidAuthenticator(MainActivity activity) => _activity = activity;
public YandexAuthenticationType YandexAuthenticationType => YandexAuthenticationType.Token;
public Task<string> ReceiveYandexCode(Uri uri, IPAddress address, int port) => throw new PlatformNotSupportedException();
public async Task<string> ReceiveYandexToken(Uri uri)
{
var completion = new TaskCompletionSource<string>();
var startWebActivity = new Intent(_activity, typeof(WebActivity));
startWebActivity.PutExtra("url", uri.ToString());
_activity.StartActivityForResult(startWebActivity, 42);
var subscription = _activity
.AuthenticationCodeReceived
.Subscribe(completion.SetResult);
var token = await completion.Task;
subscription.Dispose();
return token;
}
}
}

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

@ -0,0 +1,67 @@
using Camelotia.Services.Interfaces;
using Plugin.FilePicker;
using System.Threading.Tasks;
using System.IO;
using Android.Widget;
using Android.Content.PM;
using Android.OS;
using Android;
namespace Camelotia.Presentation.Xamarin.Droid.Services
{
public sealed class AndroidFileManager : IFileManager
{
private readonly MainActivity _activity;
public AndroidFileManager(MainActivity activity) => _activity = activity;
public async Task<(string Name, Stream Stream)> OpenRead()
{
CheckAppPermissions();
var file = await CrossFilePicker.Current.PickFile();
if (file == null) return (null, null);
var fileName = file.FileName;
var stream = file.GetStream();
return (fileName, stream);
}
public Task<Stream> OpenWrite(string name) => Task.Run(() =>
{
CheckAppPermissions();
name = name.Replace(' ', '_');
var downloads = Environment.DirectoryDownloads;
var path = Environment.GetExternalStoragePublicDirectory(downloads).AbsolutePath;
var filePath = Path.Combine(path, name);
var stream = (Stream)File.Create(filePath);
_activity.RunOnUiThread(() =>
{
var toast = $"File {name} was downloaded";
Toast.MakeText(_activity, toast, ToastLength.Long).Show();
});
return stream;
});
private void CheckAppPermissions()
{
if ((int)Build.VERSION.SdkInt < 23) return;
var manager = _activity.PackageManager;
var packageName = _activity.PackageName;
var read = Manifest.Permission.ReadExternalStorage;
var write = Manifest.Permission.WriteExternalStorage;
if (manager.CheckPermission(read, packageName) != Permission.Granted &&
manager.CheckPermission(write, packageName) != Permission.Granted)
{
_activity.RequestPermissions(new string[]
{
Manifest.Permission.ReadExternalStorage,
Manifest.Permission.WriteExternalStorage
}, 1);
}
}
}
}

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

@ -0,0 +1,54 @@
using Android.App;
using Android.Content;
using Android.OS;
using Android.Webkit;
using System;
namespace Camelotia.Presentation.Xamarin.Droid
{
[Activity(Label = "Authorization")]
public class WebActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.WebActivity);
var webView = (WebView)FindViewById(Resource.Id.web_activity_web_view);
webView.RequestFocus(Android.Views.FocusSearchDirection.Down);
webView.SetWebViewClient(new WebViewObserver(token =>
{
var data = new Intent();
data.PutExtra("token", token);
SetResult(Result.Ok, data);
Finish();
}));
var url = Intent.GetStringExtra("url");
webView.ClearHistory();
webView.ClearFormData();
webView.ClearCache(true);
CookieManager.Instance.RemoveAllCookie();
webView.LoadUrl(url);
}
public class WebViewObserver : WebViewClient
{
private readonly Action<string> _tokenReceived;
public WebViewObserver(Action<string> tokenReceived) => _tokenReceived = tokenReceived;
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
if (!url.Contains('#')) return;
var token = url
.Split('#')[1]
.Split('&')[0]
.Split('=')[1];
_tokenReceived(token);
}
}
}
}

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

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.App">
<Application.Resources>
</Application.Resources>
</Application>

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

@ -0,0 +1,17 @@
using Camelotia.Presentation.Interfaces;
using Camelotia.Presentation.Xamarin.View;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Camelotia.Presentation.Xamarin
{
public partial class App : Application
{
public App(IMainViewModel viewModel)
{
InitializeComponent();
MainPage = new MainView { ViewModel = viewModel };
}
}
}

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

@ -0,0 +1,62 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ReactiveUI.XamForms" Version="9.5.1" />
<PackageReference Include="Xamarin.Forms" Version="3.4.0.1008975" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Camelotia.Presentation\Camelotia.Presentation.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="View\DirectAuthView.xaml.cs">
<DependentUpon>DirectAuthView.xaml</DependentUpon>
</Compile>
<Compile Update="View\MainMasterView.xaml.cs">
<DependentUpon>MainMasterView.xaml</DependentUpon>
</Compile>
<Compile Update="View\MainProviderView.xaml.cs">
<DependentUpon>MainProviderView.xaml</DependentUpon>
</Compile>
<Compile Update="View\ProviderExplorerFileView.xaml.cs">
<DependentUpon>ProviderExplorerFileView.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="View\ProviderExplorerView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\AuthView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\DirectAuthView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\MainView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\OAuthView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\ProviderExplorerFileView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\ProviderView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="View\MainProviderView.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

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

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveTabbedPage
x:TypeArguments="vm:IAuthViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:view="clr-namespace:Camelotia.Presentation.Xamarin.View"
x:Class="Camelotia.Presentation.Xamarin.View.AuthView"
Title="Authentication Required">
</rxui:ReactiveTabbedPage>

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

@ -0,0 +1,35 @@
using Camelotia.Presentation.Interfaces;
using ReactiveUI.XamForms;
using ReactiveUI;
using Xamarin.Forms.Xaml;
using System.Reactive.Linq;
using System.Reactive.Disposables;
using System;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AuthView : ReactiveTabbedPage<IAuthViewModel>
{
public AuthView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.WhenAnyValue(x => x.ViewModel.SupportsDirectAuth)
.Where(supportsDirectAuth => supportsDirectAuth)
.Select(supports => new DirectAuthView { ViewModel = ViewModel.DirectAuth })
.Do(view => Children.Clear())
.Subscribe(view => Children.Add(view))
.DisposeWith(disposables);
this.WhenAnyValue(x => x.ViewModel.SupportsOAuth)
.Where(supportsOAuth => supportsOAuth)
.Select(supports => new OAuthView { ViewModel = ViewModel.OAuth })
.Do(view => Children.Clear())
.Subscribe(view => Children.Add(view))
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage
x:TypeArguments="vm:IDirectAuthViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.DirectAuthView"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:view="clr-namespace:Camelotia.Presentation.Xamarin.View"
Title="Password Authorization">
<ContentPage.Content>
<Frame VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
Margin="15">
<StackLayout>
<Label Text="Password Authorization" />
<Entry x:Name="LoginEntry" />
<Entry x:Name="PasswordEntry" IsPassword="True" />
<Button x:Name="LoginButton" Text="Login" />
</StackLayout>
</Frame>
</ContentPage.Content>
</rxui:ReactiveContentPage>

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

@ -0,0 +1,26 @@
using Camelotia.Presentation.Interfaces;
using ReactiveUI;
using ReactiveUI.XamForms;
using System.Reactive.Disposables;
using Xamarin.Forms.Xaml;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DirectAuthView : ReactiveContentPage<IDirectAuthViewModel>
{
public DirectAuthView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, x => x.Username, x => x.LoginEntry.Text)
.DisposeWith(disposables);
this.Bind(ViewModel, x => x.Password, x => x.PasswordEntry.Text)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.Login, x => x.LoginButton)
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage
x:TypeArguments="vm:IMainViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:view="clr-namespace:Camelotia.Presentation.Xamarin.View"
x:Class="Camelotia.Presentation.Xamarin.View.MainMasterView"
Title="Camelotia">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Text="Camelotia" FontSize="21" Margin="10" FontAttributes="Bold" TextColor="#311b92" />
<ListView x:Name="ProvidersView" HasUnevenRows="True" Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:IProviderViewModel">
<view:MainProviderView ViewModel="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Grid.Row="2">
<Button x:Name="RefreshButton" Text="Refresh" Margin="12" />
</StackLayout>
<ProgressBar Grid.Row="0" Grid.RowSpan="3" x:Name="LoadingBar" />
</Grid>
</rxui:ReactiveContentPage>

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

@ -0,0 +1,31 @@
using Camelotia.Presentation.Interfaces;
using ReactiveUI;
using ReactiveUI.XamForms;
using System.Reactive.Disposables;
using Xamarin.Forms.Xaml;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainMasterView : ReactiveContentPage<IMainViewModel>
{
public MainMasterView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, x => x.IsReady, x => x.ProvidersView.IsVisible)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.IsLoading, x => x.LoadingBar.IsVisible)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.LoadProviders, x => x.RefreshButton)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Providers, x => x.ProvidersView.ItemsSource)
.DisposeWith(disposables);
this.Bind(ViewModel, x => x.SelectedProvider, x => x.ProvidersView.SelectedItem)
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<rxui:ReactiveViewCell
x:TypeArguments="vm:IProviderViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.MainProviderView"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms">
<ViewCell.View>
<StackLayout Margin="10">
<Label x:Name="NameLabel" FontSize="16" FontAttributes="Bold" />
<Label x:Name="DescriptionLabel" FontAttributes="Italic" />
<StackLayout Orientation="Horizontal" Margin="0, 5">
<Label Text="Size: " />
<Label x:Name="SizeLabel" />
</StackLayout>
</StackLayout>
</ViewCell.View>
</rxui:ReactiveViewCell>

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

@ -0,0 +1,26 @@
using Camelotia.Presentation.Interfaces;
using System.Reactive.Disposables;
using Xamarin.Forms.Xaml;
using ReactiveUI.XamForms;
using ReactiveUI;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainProviderView : ReactiveViewCell<IProviderViewModel>
{
public MainProviderView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, x => x.Name, x => x.NameLabel.Text)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Description, x => x.DescriptionLabel.Text)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Size, x => x.SizeLabel.Text)
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveMasterDetailPage
x:TypeArguments="vm:IMainViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.MainView"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:views="clr-namespace:Camelotia.Presentation.Xamarin.View">
<MasterDetailPage.Master>
<views:MainMasterView ViewModel="{Binding}" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage x:Name="NavigationView" />
</MasterDetailPage.Detail>
</rxui:ReactiveMasterDetailPage>

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

@ -0,0 +1,32 @@
using Camelotia.Presentation.Interfaces;
using Xamarin.Forms.Xaml;
using ReactiveUI.XamForms;
using ReactiveUI;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainView : ReactiveMasterDetailPage<IMainViewModel>
{
public MainView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.WhenAnyValue(x => x.ViewModel.SelectedProvider)
.Select(provider => false)
.Subscribe(x => IsPresented = x)
.DisposeWith(disposables);
this.WhenAnyValue(x => x.ViewModel.SelectedProvider)
.Where(provider => provider != null)
.Select(x => new ProviderView { ViewModel = ViewModel.SelectedProvider })
.Subscribe(view => NavigationView.PushAsync(view))
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage
x:TypeArguments="vm:IOAuthViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.OAuthView"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:view="clr-namespace:Camelotia.Presentation.Xamarin.View"
Title="OAuth Authorization">
<ContentPage.Content>
<Frame VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
Margin="15">
<StackLayout>
<Label Text="OAuth Authorization" />
<Button x:Name="LoginButton" Text="Authorize" />
</StackLayout>
</Frame>
</ContentPage.Content>
</rxui:ReactiveContentPage>

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

@ -0,0 +1,22 @@
using Camelotia.Presentation.Interfaces;
using System.Reactive.Disposables;
using Xamarin.Forms.Xaml;
using ReactiveUI.XamForms;
using ReactiveUI;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class OAuthView : ReactiveContentPage<IOAuthViewModel>
{
public OAuthView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.BindCommand(ViewModel, x => x.Login, x => x.LoginButton)
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<rxui:ReactiveViewCell
x:TypeArguments="vm:FileModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.ProviderExplorerFileView"
xmlns:vm="clr-namespace:Camelotia.Services.Models;assembly=Camelotia.Services"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms">
<ViewCell.View>
<StackLayout Margin="8" Grid.Column="0">
<Label x:Name="NameLabel" FontSize="16" />
<Label x:Name="SizeLabel" FontAttributes="Bold"/>
</StackLayout>
</ViewCell.View>
</rxui:ReactiveViewCell>

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

@ -0,0 +1,24 @@
using Camelotia.Services.Models;
using ReactiveUI;
using ReactiveUI.XamForms;
using System.Reactive.Disposables;
using Xamarin.Forms.Xaml;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ProviderExplorerFileView : ReactiveViewCell<FileModel>
{
public ProviderExplorerFileView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, x => x.Name, x => x.NameLabel.Text)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Size, x => x.SizeLabel.Text)
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage
x:TypeArguments="vm:IProviderViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.ProviderExplorerView"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:view="clr-namespace:Camelotia.Presentation.Xamarin.View">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
x:Name="BackButton"
Text="Back" />
<Label Grid.Column="1"
x:Name="PathLabel"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
FontSize="14"
Margin="15, 0" />
<Button Grid.Column="2"
x:Name="OpenButton"
Text="Open" />
<Button Grid.Column="3"
x:Name="RefreshButton"
Text="Refresh" />
</Grid>
<ListView Grid.Row="1"
x:Name="FilesListView"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<view:ProviderExplorerFileView ViewModel="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="2">
<StackLayout Orientation="Horizontal"
HorizontalOptions="StartAndExpand"
Margin="10, 5">
<Label Text="You selected: " />
<Label x:Name="SelectedFileNameLabel" TextColor="Accent" />
</StackLayout>
</Grid>
<Grid Grid.Row="3" Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
x:Name="LogoutButton"
Text="Logout" />
<Button Grid.Column="1"
x:Name="DeleteButton"
Text="Delete" />
<Button Grid.Column="3"
x:Name="UploadButton"
Text="Upload" />
<Button Grid.Column="4"
x:Name="DownloadButton"
Text="Download" />
</Grid>
</Grid>
</ContentPage.Content>
</rxui:ReactiveContentPage>

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

@ -0,0 +1,56 @@
using Camelotia.Presentation.Interfaces;
using System.Reactive.Disposables;
using Xamarin.Forms.Xaml;
using ReactiveUI.XamForms;
using ReactiveUI;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ProviderExplorerView : ReactiveContentPage<IProviderViewModel>
{
public ProviderExplorerView()
{
InitializeComponent();
this.WhenActivated(disposables =>
{
this.BindCommand(ViewModel, x => x.Back, x => x.BackButton)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.CurrentPath, x => x.PathLabel.Text)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.Refresh, x => x.RefreshButton)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.Open, x => x.OpenButton)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Files, x => x.FilesListView.ItemsSource)
.DisposeWith(disposables);
this.Bind(ViewModel, x => x.SelectedFile, x => x.FilesListView.SelectedItem)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.IsLoading, x => x.FilesListView.IsRefreshing)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Refresh, x => x.FilesListView.RefreshCommand)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.CanLogout, x => x.LogoutButton.IsVisible)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.Logout, x => x.LogoutButton)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.DeleteSelectedFile, x => x.DeleteButton)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.SelectedFile.Name, x => x.SelectedFileNameLabel.Text)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.UploadToCurrentPath, x => x.UploadButton)
.DisposeWith(disposables);
this.BindCommand(ViewModel, x => x.DownloadSelectedFile, x => x.DownloadButton)
.DisposeWith(disposables);
this.OneWayBind(ViewModel, x => x.Name, x => x.Title)
.DisposeWith(disposables);
});
}
}
}

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

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveNavigationPage
x:TypeArguments="vm:IProviderViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Camelotia.Presentation.Xamarin.View.ProviderView"
xmlns:vm="clr-namespace:Camelotia.Presentation.Interfaces;assembly=Camelotia.Presentation"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:view="clr-namespace:Camelotia.Presentation.Xamarin.View">
</rxui:ReactiveNavigationPage>

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

@ -0,0 +1,46 @@
using Camelotia.Presentation.Interfaces;
using Xamarin.Forms.Xaml;
using ReactiveUI.XamForms;
using ReactiveUI;
using System.Reactive.Linq;
using System.Reactive.Disposables;
using System;
using Xamarin.Forms;
using System.Linq;
namespace Camelotia.Presentation.Xamarin.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ProviderView : ReactiveNavigationPage<IProviderViewModel>
{
public ProviderView()
{
InitializeComponent();
SetHasNavigationBar(this, false);
this.WhenActivated(disposables =>
{
this.WhenAnyValue(x => x.ViewModel.Auth.IsAuthenticated)
.Where(authenticated => authenticated)
.DistinctUntilChanged()
.Select(x => new ProviderExplorerView { ViewModel = ViewModel })
.Subscribe(NavigateWithoutBackStack)
.DisposeWith(disposables);
this.WhenAnyValue(x => x.ViewModel.Auth.IsAuthenticated)
.Where(authenticated => !authenticated)
.DistinctUntilChanged()
.Select(x => new AuthView { ViewModel = ViewModel.Auth })
.Subscribe(NavigateWithoutBackStack)
.DisposeWith(disposables);
});
}
private async void NavigateWithoutBackStack(Page page)
{
if (Navigation.NavigationStack.Any())
Navigation.InsertPageBefore(page, Navigation.NavigationStack.First());
else await Navigation.PushAsync(page);
await Navigation.PopToRootAsync();
}
}
}

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

@ -9,10 +9,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ReactiveUI" Version="9.1.1" />
<PackageReference Include="ReactiveUI.Fody" Version="9.1.1" />
<PackageReference Include="DynamicData" Version="6.6.1.2507" />
<PackageReference Include="Refit" Version="4.6.48" />
<PackageReference Include="ReactiveUI" Version="9.5.1" />
<PackageReference Include="ReactiveUI.Fody" Version="9.5.1" />
<PackageReference Include="DynamicData" Version="6.7.1.2534" />
</ItemGroup>
</Project>

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

@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="akavache" Version="6.0.31" />
<PackageReference Include="System.Reactive" Version="4.0.0" />
<PackageReference Include="akavache" Version="6.2.3" />
<PackageReference Include="System.Reactive" Version="4.1.2" />
<PackageReference Include="VkNet" Version="1.40.0" />
</ItemGroup>

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

@ -13,6 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Camelotia.Presentation.Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Camelotia.Presentation.Uwp", "Camelotia.Presentation.Uwp\Camelotia.Presentation.Uwp.csproj", "{359DA275-5214-457A-887D-DDBD9763667B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Camelotia.Presentation.Xamarin.Droid", "Camelotia.Presentation.Xamarin.Android\Camelotia.Presentation.Xamarin.Droid.csproj", "{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Camelotia.Presentation.Xamarin", "Camelotia.Presentation.Xamarin\Camelotia.Presentation.Xamarin.csproj", "{F019715C-675A-463B-AF36-58585F6FA7DF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -133,6 +137,56 @@ Global
{359DA275-5214-457A-887D-DDBD9763667B}.Release|x86.ActiveCfg = Release|x86
{359DA275-5214-457A-887D-DDBD9763667B}.Release|x86.Build.0 = Release|x86
{359DA275-5214-457A-887D-DDBD9763667B}.Release|x86.Deploy.0 = Release|x86
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|ARM.Build.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|ARM.Deploy.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|ARM64.Build.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|ARM64.Deploy.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|x64.ActiveCfg = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|x64.Build.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|x64.Deploy.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|x86.ActiveCfg = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|x86.Build.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Debug|x86.Deploy.0 = Debug|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|Any CPU.Build.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|Any CPU.Deploy.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|ARM.ActiveCfg = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|ARM.Build.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|ARM.Deploy.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|ARM64.ActiveCfg = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|ARM64.Build.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|ARM64.Deploy.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|x64.ActiveCfg = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|x64.Build.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|x64.Deploy.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|x86.ActiveCfg = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|x86.Build.0 = Release|Any CPU
{24CE3C36-16B9-426D-AA0C-5961ECF8D88C}.Release|x86.Deploy.0 = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|ARM.ActiveCfg = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|ARM.Build.0 = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|ARM64.Build.0 = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|x64.ActiveCfg = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|x64.Build.0 = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|x86.ActiveCfg = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Debug|x86.Build.0 = Debug|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|Any CPU.Build.0 = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|ARM.ActiveCfg = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|ARM.Build.0 = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|ARM64.ActiveCfg = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|ARM64.Build.0 = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|x64.ActiveCfg = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|x64.Build.0 = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|x86.ActiveCfg = Release|Any CPU
{F019715C-675A-463B-AF36-58585F6FA7DF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

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

@ -6,7 +6,7 @@ The app runs on Windows, Linux, MacOS, XBox, Surface Hub and HoloLens.
You can compile .NET Standard libraries, run tests and run an Avalonia application on Windows, Linux and macOS operating systems. Make sure you have latest [.NET Core SDK](https://dot.net/) installed.
<img src="./Camelotia.png" width="500">
<img src="./UiAvalonia.png" width="500">
```sh
# Linux or MacOS shell
@ -21,7 +21,13 @@ On Windows, double-click the `./run.bat` file.
You can compile Universal Windows Platform Camelotia app only on latest Windows 10. Make sure you have latest [Microsoft Visual Studio](https://visualstudio.microsoft.com/) installed. Make sure the "Universal Application Development" section is checked in [Visual Studio Installer](https://visualstudio.microsoft.com/ru/vs/).
<img src="./Universal.jpg" width="500">
<img src="./UiWindows.jpg" width="500">
## Compiling Xamarin Forms app
To compile the Xamarin Forms Android application, you need to install appropriate Android SDK v8.1. This can be achieved by using [Visual Studio Installer](https://visualstudio.microsoft.com/ru/vs/) and selecting "Mobile Development" section there.
<img src="./UiAndroid1.png" width="220"> <img src="./UiAndroid2.png" width="220">
### Adding Custom Providers
@ -29,8 +35,10 @@ File system providers are located at `./Camelotia.Services/Providers/`. To add a
### Technologies and Tools Used
- <a href="https://reactiveui.net/">ReactiveUI</a>
- <a href="http://github.com/avaloniaui">AvaloniaUI</a>
- <a href="https://github.com/nsubstitute/NSubstitute">NSubstitute</a>
- <a href="https://github.com/fluentassertions/fluentassertions">FluentAssertions</a>
- <a href="https://reactiveui.net/">ReactiveUI</a> modern MVVM framework
- <a href="http://github.com/avaloniaui">AvaloniaUI</a> cross-platform GUI framework
- <a href="https://github.com/nsubstitute/NSubstitute">NSubstitute</a> mocking library
- <a href="https://github.com/fluentassertions/fluentassertions">FluentAssertions</a> assertions library
- <a href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/">Xamarin.Forms</a> mobile GUI framework
- <a href="https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide">Universal Windows Platform</a>
- <a href="https://www.jetbrains.com/rider/">JetBrains Rider IDE</a>

Двоичные данные
UiAndroid1.png Normal file

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

После

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

Двоичные данные
UiAndroid2.png Normal file

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

После

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

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

До

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

После

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

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

До

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

После

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

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

@ -5,7 +5,7 @@ jobs:
variables:
buildConfiguration: 'Release'
steps:
- script: dotnet build --configuration $(buildConfiguration)
- script: dotnet build --configuration $(buildConfiguration) $(Build.SourcesDirectory)/Camelotia.Presentation.Avalonia/Camelotia.Presentation.Avalonia.csproj
displayName: 'Linux Build'
- script: dotnet test --logger trx Camelotia.Presentation.Tests
displayName: 'Linux Unit Tests'
@ -20,7 +20,7 @@ jobs:
variables:
buildConfiguration: 'Release'
steps:
- script: dotnet build --configuration $(buildConfiguration)
- script: dotnet build --configuration $(buildConfiguration) $(Build.SourcesDirectory)/Camelotia.Presentation.Avalonia/Camelotia.Presentation.Avalonia.csproj
displayName: 'Windows Build'
- script: |
mkdir $(Build.SourcesDirectory)\results