Initial code upload.
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"ExpandedNodes": [
|
"ExpandedNodes": [
|
||||||
""
|
"\\Source",
|
||||||
|
"\\Source\\Sketch360.XPlat.Android"
|
||||||
],
|
],
|
||||||
"PreviewInSolutionExplorer": false
|
"PreviewInSolutionExplorer": false
|
||||||
}
|
}
|
26
README.md
|
@ -1,9 +1,31 @@
|
||||||
# Sketch 360
|
# Sketch 360 a Microsoft Garage Project
|
||||||
|
A Cross Platform 360 Degree Panoramic Sketching App
|
||||||
|
|
||||||
|
- Install from the [Google Play Marketplace](https://play.google.com/store/apps/details?id=com.microsoft.garage.sketch360app)
|
||||||
|
- Install from the [Microsoft Store](https://www.microsoft.com/en-us/p/sketch-360/9p89s2qlh11t)
|
||||||
|
|
||||||
|
Sketch 360 was built by [Michael Scherotter](https://github.com/mscherotter) and [originally released](https://www.microsoft.com/en-us/garage/blog/2018/11/finally-a-way-to-sketch-360-degree-vr-scenes/) as a Microsoft Garage project in 2018 as a UWP app for Microsoft Windows as is available today for free in the [Microsoft Store](https://www.microsoft.com/en-us/p/sketch-360/9p89s2qlh11t). This is a new version of Sketch 360 built to take advantage of pen-enabled devices that have two screens like the [Microsoft Surface Duo](https://www.microsoft.com/en-us/surface/devices/surface-duo) but it should work on most Android devices as well.
|
||||||
|
|
||||||
|
## Examples of Sketches created with Sketch 360
|
||||||
|
- [360 Drawings](https://lightroom.adobe.com/shares/21b9e652ff4e46ef86130478cbb50abf)
|
||||||
|
- [360 Videos](https://studio.youtube.com/video/AImRmQYN_hk/edit)
|
||||||
|
- [Virtual Flight 360 Sketch](https://youtu.be/pTFVXD1v3zQ)
|
||||||
|
## Sketch 360 on the Microsoft Surface Duo
|
||||||
|
![Sketch 360 on the Microsoft Surface Duo](mobile.jpg)
|
||||||
|
|
||||||
|
## Platforms
|
||||||
|
- ***Android**: Complete with enhancements for Microsoft Surface Duo (dual screen & pressure-sensitive pen support)
|
||||||
|
- **iOS**: In-progress
|
||||||
|
- **Windows**: In-progress but not prioritized as the native Windows version is already available.
|
||||||
|
|
||||||
|
## Technology
|
||||||
|
- Xamarin Forms
|
||||||
|
- SkiaSharp
|
||||||
|
- Babylon JS
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
[How to contribute](Contributing.md)
|
[How to contribute](Contributing.md)
|
||||||
|
|
||||||
## Open Source
|
|
||||||
[Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct)
|
[Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct)
|
||||||
|
|
||||||
## Trademarks
|
## Trademarks
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Don't use tabs for indentation.
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
# (Please don't specify an indent_size here; that has too many unintended consequences.)
|
||||||
|
|
||||||
|
# Code files
|
||||||
|
[*.{cs,csx,vb,vbx}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = false
|
||||||
|
charset = utf-8-bom
|
||||||
|
trim_trailing_whitespace = true
|
|
@ -0,0 +1,199 @@
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# Visual Studio (>=2015) project-specific, machine local files
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
*.userprefs
|
||||||
|
*.ide-shm
|
||||||
|
*.ide-wal
|
||||||
|
*.dll
|
||||||
|
*.pdb
|
||||||
|
classes.zip
|
||||||
|
# ignore Xamarin.Android Resource.Designer.cs files
|
||||||
|
**/*.Droid/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
**/*.Android/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
**/Android/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
**/Droid/**/[Rr]esource.[Dd]esigner.cs
|
||||||
|
|
||||||
|
# Xamarin Components
|
||||||
|
Components/
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
x64/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
#NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding addin-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
*.ncrunch*
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
|
||||||
|
# NuGet Packages Directory
|
||||||
|
packages/
|
||||||
|
*.nuget.targets
|
||||||
|
*.lock.json
|
||||||
|
*.nuget.props
|
||||||
|
|
||||||
|
## TODO: If the tool you use requires repositories.config uncomment the next line
|
||||||
|
#!packages/repositories.config
|
||||||
|
|
||||||
|
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
||||||
|
# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
|
||||||
|
!packages/build/
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
sql/
|
||||||
|
*.Cache
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
.DS_Store
|
||||||
|
*.bak
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file to a newer
|
||||||
|
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
.vscode/settings.json
|
После Ширина: | Высота: | Размер: 841 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<add key="nugetLocal" value="nuget" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: Xamarin.Forms.Dependency(typeof(Sketch360.XPlat.Droid.BaseUrl))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
public class BaseUrl : IBaseUrl
|
||||||
|
{
|
||||||
|
public string GetBase()
|
||||||
|
{
|
||||||
|
return "file:///android_asset/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream GetDrawableImageStream(string filename)
|
||||||
|
{
|
||||||
|
string resourceID = "Sketch360.XPlat.Droid.Resources.drawable." + filename;
|
||||||
|
|
||||||
|
var assembly = typeof(BaseUrl).GetTypeInfo().Assembly;
|
||||||
|
|
||||||
|
//var names = assembly.GetManifestResourceNames();
|
||||||
|
|
||||||
|
return assembly.GetManifestResourceStream(resourceID);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using Android.Content;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(Sketch360.XPlat.Droid.CustomWebViewRenderer))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Custom WebView renderer that allows local file URLs
|
||||||
|
/// </summary>
|
||||||
|
public class CustomWebViewRenderer : WebViewRenderer
|
||||||
|
{
|
||||||
|
public CustomWebViewRenderer(Context context) : base(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
|
||||||
|
{
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if (Control != null)
|
||||||
|
Control.Settings.AllowUniversalAccessFromFileURLs = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
После Ширина: | Высота: | Размер: 841 KiB |
|
@ -0,0 +1,33 @@
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Graphics;
|
||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(typeof(Sketch360.XPlat.InkCanvas), typeof(Sketch360.XPlat.Droid.InkCanvasRenderer))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// InkCanvas renderer for Android
|
||||||
|
/// </summary>
|
||||||
|
public class InkCanvasRenderer : ViewRenderer
|
||||||
|
{
|
||||||
|
Paint _inkPaint = new Paint();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the InkCanvasRenderer class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">the context</param>
|
||||||
|
public InkCanvasRenderer(Context context) : base(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
if (canvas == null) throw new ArgumentNullException(nameof(canvas));
|
||||||
|
|
||||||
|
canvas.DrawText("InkCanvas", 100, 100, _inkPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
using Xamarin.Duo.Forms.Samples;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: Dependency(typeof(LayoutService))]
|
||||||
|
namespace Xamarin.Duo.Forms.Samples
|
||||||
|
{
|
||||||
|
public class LayoutService : LayoutServiceBase, ILayoutService
|
||||||
|
{
|
||||||
|
public override Point? GetLocationOnScreen(VisualElement visualElement)
|
||||||
|
{
|
||||||
|
var view = Platform.GetRenderer(visualElement);
|
||||||
|
|
||||||
|
if (view?.View == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int[] location = new int[2];
|
||||||
|
view.View.GetLocationOnScreen(location);
|
||||||
|
return new Point(view.View.Context.FromPixels(location[0]), view.View.Context.FromPixels(location[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.PM;
|
||||||
|
using Android.OS;
|
||||||
|
using Android.Runtime;
|
||||||
|
//using Microsoft.AppCenter.Distribute;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
//using Xamarin.Duo.Forms.Samples;
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Android main activity
|
||||||
|
/// </summary>
|
||||||
|
[Activity(Label = "Sketch 360", Icon = "@drawable/logo", Theme = "@style/MainTheme", MainLauncher = true,
|
||||||
|
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
|
||||||
|
[IntentFilter(new[] { Intent.ActionView, Intent.ActionMain, Intent.ActionEdit },
|
||||||
|
//Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault, Intent.CategoryOpenable, Intent.CategoryLauncher},
|
||||||
|
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
|
||||||
|
|
||||||
|
DataMimeTypes = new[] { "application/x-sketch360", "application/octet-stream" },
|
||||||
|
DataScheme = "file",
|
||||||
|
Label = "Sketch 360",
|
||||||
|
DataPathPatterns = new[]
|
||||||
|
{
|
||||||
|
"*.sketch360",
|
||||||
|
".*\\.sketch360",
|
||||||
|
".*\\..*\\.sketch360",
|
||||||
|
".*\\..*\\..*\\.sketch360",
|
||||||
|
".*\\..*\\..*\\..*\\.sketch360"
|
||||||
|
},
|
||||||
|
Icon = "@drawable/logo",
|
||||||
|
DataHost = "*")]
|
||||||
|
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||||
|
{
|
||||||
|
internal static MainActivity Instance { get; private set; }
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "<Pending>")]
|
||||||
|
protected override void OnCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
global::Xamarin.Forms.DualScreen.DualScreenService.Init(this);
|
||||||
|
Remote.MainActivity = this;
|
||||||
|
TabLayoutResource = Resource.Layout.Tabbar;
|
||||||
|
ToolbarResource = Resource.Layout.Toolbar;
|
||||||
|
|
||||||
|
Instance = this;
|
||||||
|
|
||||||
|
base.OnCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
|
||||||
|
|
||||||
|
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
|
||||||
|
|
||||||
|
//Distribute.SetEnabledForDebuggableBuild(true);
|
||||||
|
|
||||||
|
LoadApplication(new App());
|
||||||
|
|
||||||
|
//enable Android webview debugging
|
||||||
|
global::Android.Webkit.WebView.SetWebContentsDebuggingEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
|
||||||
|
{
|
||||||
|
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
|
||||||
|
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||||
|
{
|
||||||
|
base.OnActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (requestCode == 101)
|
||||||
|
{
|
||||||
|
switch (resultCode)
|
||||||
|
{
|
||||||
|
case Result.Ok:
|
||||||
|
if (data != null && data.Data != null)
|
||||||
|
{
|
||||||
|
_fileName?.SetResult(data.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_fileName?.SetResult(null);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_fileName?.SetResult(null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskCompletionSource<Android.Net.Uri> _fileName;
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "<Pending>")]
|
||||||
|
public Task<Android.Net.Uri> OpenSaveFileDialog(string currentFileName)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(Intent.ActionCreateDocument);
|
||||||
|
intent.AddCategory(Intent.CategoryOpenable);
|
||||||
|
intent.SetType("application/octet-stream");
|
||||||
|
intent.PutExtra(Intent.ExtraTitle, currentFileName);
|
||||||
|
_fileName = new TaskCompletionSource<Android.Net.Uri>();
|
||||||
|
StartActivityForResult(intent, 101);
|
||||||
|
return _fileName.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
using Android.Graphics;
|
||||||
|
using Sketch360.XPlat.Data;
|
||||||
|
using SkiaSharp;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
[assembly: Dependency(typeof(Sketch360.XPlat.Droid.OffscreenRenderer))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
public class OffscreenRenderer : IOffscreenRenderer
|
||||||
|
{
|
||||||
|
public void Render(float width, float height, IEnumerable<InkStroke> strokes)
|
||||||
|
{
|
||||||
|
using (var bitmap = Bitmap.CreateBitmap((int) width, (int) height, Bitmap.Config.Argb8888))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var surface = SKSurface.Create((int) width, (int) height, SKColorType.Rgba8888, SKAlphaType.Premul, bitmap.LockPixels(), (int) width * 4))
|
||||||
|
{
|
||||||
|
var skcanvas = surface.Canvas;
|
||||||
|
|
||||||
|
skcanvas.Draw(strokes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
bitmap.UnlockPixels();
|
||||||
|
}
|
||||||
|
|
||||||
|
//bitmap.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,315 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using Android;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Content.PM;
|
||||||
|
using Android.Media;
|
||||||
|
using Android.Provider;
|
||||||
|
using Android.Support.V4.App;
|
||||||
|
using Sketch360.XPlat.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AppCenter.Crashes;
|
||||||
|
|
||||||
|
[assembly: Dependency(typeof(Sketch360.XPlat.Droid.PhotoLibrary))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Android Photo library
|
||||||
|
/// </summary>
|
||||||
|
public class PhotoLibrary : IPhotoLibrary
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
/// <summary>
|
||||||
|
/// XMP Metadata with equirectangular Projection property
|
||||||
|
/// </summary>
|
||||||
|
private const string Xmp = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\"><rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"><rdf:Description xmlns:prefix0=\"http://ns.google.com/photos/1.0/panorama/\"><prefix0:ProjectionType>equirectangular</prefix0:ProjectionType></rdf:Description></rdf:RDF></x:xmpmeta>";
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
/// <summary>
|
||||||
|
/// Save a photo to the photo library with equirectangular metadata.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="folder"></param>
|
||||||
|
/// <param name="filename"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>")]
|
||||||
|
public async Task<bool> SavePhotoAsync(byte[] data, string folder, string filename, Page page)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!(await RequestPermissionsAsync(page).ConfigureAwait(false)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string tempFilename = await SaveToTempFileAsync(data, filename).ConfigureAwait(false);
|
||||||
|
|
||||||
|
AddExifData(tempFilename);
|
||||||
|
|
||||||
|
var uri = await WriteContentValuesAsync(tempFilename, filename, "image/jpeg").ConfigureAwait(false);
|
||||||
|
|
||||||
|
System.IO.File.Delete(tempFilename);
|
||||||
|
|
||||||
|
var newFilename = GetPath(uri);
|
||||||
|
|
||||||
|
await LaunchFileAsync(newFilename).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Crashes.TrackError(e);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<bool> RequestPermissionsAsync(Page page)
|
||||||
|
{
|
||||||
|
Task<PermissionStatus>[] permissions = new[]
|
||||||
|
{
|
||||||
|
new Permissions.StorageWrite().RequestAsync(),
|
||||||
|
new Permissions.Photos().RequestAsync(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = await Task.WhenAll(permissions).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if(results.Any(x => x != PermissionStatus.Granted))
|
||||||
|
{
|
||||||
|
if (page != null)
|
||||||
|
{
|
||||||
|
await page.DisplayAlert(
|
||||||
|
Resources.AndroidResources.PermissionsTitle,
|
||||||
|
Resources.AndroidResources.PermssionsMessage,
|
||||||
|
Resources.AndroidResources.OK).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetPath(Android.Net.Uri uri)
|
||||||
|
{
|
||||||
|
string path = null;
|
||||||
|
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
|
String[] projection = { MediaStore.MediaColumns.Data };
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
|
||||||
|
var metaCursor = MainActivity.Instance.ContentResolver.Query(uri, projection, null, null, null);
|
||||||
|
if (metaCursor != null)
|
||||||
|
{
|
||||||
|
using (metaCursor)
|
||||||
|
{
|
||||||
|
if (metaCursor.MoveToFirst())
|
||||||
|
{
|
||||||
|
path = metaCursor.GetString(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>")]
|
||||||
|
public async Task<bool> SaveSketchAsync(byte[] data, string folder, string filename, Page page)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!(await RequestPermissionsAsync(page).ConfigureAwait(false)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempFilename = await SaveToTempFileAsync(data, filename).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var urilocation = await WriteDownloadValuesAsync(tempFilename, filename, "application/x-sketch360").ConfigureAwait(false);
|
||||||
|
|
||||||
|
result = urilocation != null;
|
||||||
|
|
||||||
|
//var documentsDirectory = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
|
||||||
|
|
||||||
|
//var fullPath = await WriteToFolderAsync(data, filename, documentsDirectory).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
||||||
|
//MediaScannerConnection.ScanFile(MainActivity.Instance,
|
||||||
|
// new string[] { fullPath },
|
||||||
|
// new string[] { "application/x-sketch360" }, null);
|
||||||
|
|
||||||
|
|
||||||
|
//var fullPath = await WriteToSpecialFolderAsync(tempFilename, filename, Environment.SpecialFolder.MyDocuments).ConfigureAwait(false);
|
||||||
|
|
||||||
|
System.IO.File.Delete(tempFilename);
|
||||||
|
|
||||||
|
//await LaunchFileAsync(fullPath).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Crashes.TrackError(e);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation
|
||||||
|
//private static async Task<string> WriteToSpecialFolderAsync(string tempFilename, string filename, Java.IO.File folder)
|
||||||
|
//{
|
||||||
|
// folder.Mkdirs();
|
||||||
|
|
||||||
|
// var filePath = Path.Combine(folder.AbsolutePath, filename);
|
||||||
|
|
||||||
|
// var data = System.IO.File.ReadAllBytes(tempFilename);
|
||||||
|
|
||||||
|
// using (var stream = new Java.IO.FileOutputStream(filePath))
|
||||||
|
// {
|
||||||
|
// await stream.WriteAsync(data).ConfigureAwait(false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return filePath;
|
||||||
|
// //var folder = System.Environment.GetFolderPath(specialFolder);
|
||||||
|
|
||||||
|
// // System.IO.Directory.CreateDirectory(folder);
|
||||||
|
|
||||||
|
// // var myPicturesFilename = Path.Combine(folder, filename);
|
||||||
|
|
||||||
|
// // using (var stream = System.IO.File.Create(myPicturesFilename))
|
||||||
|
// // using (var tempStream = System.IO.File.OpenRead(tempFilename))
|
||||||
|
// // {
|
||||||
|
// // await tempStream.CopyToAsync(stream).ConfigureAwait(false);
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // return myPicturesFilename;
|
||||||
|
//}
|
||||||
|
//private static async Task<string> WriteToSpecialFolderAsync(string tempFilename, string filename, Environment.SpecialFolder specialFolder)
|
||||||
|
//{
|
||||||
|
// var folder = System.Environment.GetFolderPath(specialFolder);
|
||||||
|
|
||||||
|
// System.IO.Directory.CreateDirectory(folder);
|
||||||
|
|
||||||
|
// var myPicturesFilename = Path.Combine(folder, filename);
|
||||||
|
|
||||||
|
// using (var stream = System.IO.File.Create(myPicturesFilename))
|
||||||
|
// using (var tempStream = System.IO.File.OpenRead(tempFilename))
|
||||||
|
// {
|
||||||
|
// await tempStream.CopyToAsync(stream).ConfigureAwait(false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return myPicturesFilename;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private static async Task<string> WriteToFolderAsync(byte[] data, string filename, Java.IO.File folder)
|
||||||
|
//{
|
||||||
|
// folder.Mkdirs();
|
||||||
|
|
||||||
|
// var filePath = Path.Combine(folder.AbsolutePath, filename);
|
||||||
|
|
||||||
|
// using (var stream = new Java.IO.FileOutputStream(filePath))
|
||||||
|
// {
|
||||||
|
// await stream.WriteAsync(data).ConfigureAwait(false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return filePath;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>")]
|
||||||
|
private static async Task LaunchFileAsync(string filename)
|
||||||
|
{
|
||||||
|
//var fullPath = $"/storage/emulated/0/DCIM/Sketch360/{filename}";
|
||||||
|
|
||||||
|
var request = new OpenFileRequest
|
||||||
|
{
|
||||||
|
|
||||||
|
File = new ReadOnlyFile(filename)
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Launcher.OpenAsync(request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Crashes.TrackError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<string> SaveToTempFileAsync(byte[] data, string filename)
|
||||||
|
{
|
||||||
|
var tempFilename = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), filename);
|
||||||
|
|
||||||
|
using (var stream = System.IO.File.Create(tempFilename))
|
||||||
|
{
|
||||||
|
await stream.WriteAsync(data).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tempFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddExifData(string filename)
|
||||||
|
{
|
||||||
|
using var exif = new ExifInterface(filename);
|
||||||
|
|
||||||
|
exif.SetAttribute(ExifInterface.TagXmp, Xmp);
|
||||||
|
|
||||||
|
exif.SetAttribute(ExifInterface.TagSoftware, "Sketch 360");
|
||||||
|
|
||||||
|
exif.SaveAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Android.Net.Uri> WriteDownloadValuesAsync(string tempfilename, string filename, string mimeType)
|
||||||
|
{
|
||||||
|
using var stream = System.IO.File.OpenRead(tempfilename);
|
||||||
|
using var contentValues = new ContentValues();
|
||||||
|
contentValues.Put(MediaStore.DownloadColumns.DisplayName, filename);
|
||||||
|
contentValues.Put(MediaStore.DownloadColumns.MimeType, mimeType);
|
||||||
|
|
||||||
|
|
||||||
|
var uri = await MainActivity.Instance.OpenSaveFileDialog(filename).ConfigureAwait(true);
|
||||||
|
|
||||||
|
//var uri = MainActivity.Instance.ContentResolver.Insert(MediaStore.Downloads.ExternalContentUri, contentValues);
|
||||||
|
|
||||||
|
if (uri == null) return null;
|
||||||
|
|
||||||
|
using (var outputStream = MainActivity.Instance.ContentResolver.OpenOutputStream(uri))
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(outputStream).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Android.Net.Uri> WriteContentValuesAsync(string tempfilename, string filename, string mimeType)
|
||||||
|
{
|
||||||
|
using var stream = System.IO.File.OpenRead(tempfilename);
|
||||||
|
using var contentValues = new ContentValues();
|
||||||
|
contentValues.Put(MediaStore.MediaColumns.DisplayName, filename);
|
||||||
|
contentValues.Put(MediaStore.MediaColumns.MimeType, mimeType);
|
||||||
|
|
||||||
|
var uri = MainActivity.Instance.ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, contentValues);
|
||||||
|
|
||||||
|
if (uri == null) return null;
|
||||||
|
|
||||||
|
using (var outputStream = MainActivity.Instance.ContentResolver.OpenOutputStream(uri))
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(outputStream).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="2.1" package="com.microsoft.garage.sketch360app" android:installLocation="auto" android:versionCode="2">
|
||||||
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" />
|
||||||
|
<application android:label="Sketch 360" android:icon="@mipmap/launcher_foreground" android:requestLegacyExternalStorage="true"></application>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
</manifest>
|
|
@ -0,0 +1,36 @@
|
||||||
|
using Android.App;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Sketch 360")]
|
||||||
|
[assembly: AssemblyDescription("360 sketching")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Microsoft Garage")]
|
||||||
|
[assembly: AssemblyProduct("Sketch360.XPlat.Android")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2020 Microsoft Corporation")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
|
|
||||||
|
// Add some common permissions, these can be removed if not needed
|
||||||
|
[assembly: UsesPermission(Android.Manifest.Permission.Internet)]
|
||||||
|
[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
|
||||||
|
[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage)]
|
||||||
|
[assembly: NeutralResourcesLanguage("en-US")]
|
|
@ -0,0 +1,174 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using Android.App;
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Webkit;
|
||||||
|
using Android.Widget;
|
||||||
|
using Microsoft.ConnectedDevices;
|
||||||
|
using Sketch360.XPlat.Data;
|
||||||
|
using Sketch360.XPlat.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
[assembly: Xamarin.Forms.Dependency(typeof(Sketch360.XPlat.Droid.Remote))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Remote system using Project Rome to send data to other systems
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Remote : IRemote, IDisposable
|
||||||
|
{
|
||||||
|
const string AppId = "f2d1007c-dbd7-4d32-a488-93a6bdbc12b9";
|
||||||
|
|
||||||
|
private WebView _webView;
|
||||||
|
internal Dialog _authDialog;
|
||||||
|
private RemoteSystemWatcher _remoteSystemWatcher;
|
||||||
|
|
||||||
|
public Remote()
|
||||||
|
{
|
||||||
|
RemoteSystems = new ObservableCollection<RemoteSystemInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MainActivity MainActivity { get; set; }
|
||||||
|
|
||||||
|
public ObservableCollection<RemoteSystemInfo> RemoteSystems { get; private set; }
|
||||||
|
|
||||||
|
public async Task<bool> ConnectAsync()
|
||||||
|
{
|
||||||
|
Platform.FetchAuthCode += Platform_FetchAuthCode;
|
||||||
|
|
||||||
|
if (await Platform.InitializeAsync(MainActivity, AppId).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
DiscoverDevices();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DiscoverDevices()
|
||||||
|
{
|
||||||
|
_remoteSystemWatcher = RemoteSystem.CreateWatcher();
|
||||||
|
|
||||||
|
_remoteSystemWatcher.RemoteSystemAdded += RemoteSystemAdded;
|
||||||
|
_remoteSystemWatcher.RemoteSystemRemoved += RemoteSystemRemoved;
|
||||||
|
_remoteSystemWatcher.RemoteSystemUpdated += RemoteSystemUpdated;
|
||||||
|
|
||||||
|
_remoteSystemWatcher.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoteSystemUpdated(RemoteSystemWatcher watcher, RemoteSystemUpdatedEventArgs args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoteSystemRemoved(RemoteSystemWatcher watcher, RemoteSystemRemovedEventArgs args)
|
||||||
|
{
|
||||||
|
MainActivity.RunOnUiThread(delegate
|
||||||
|
{
|
||||||
|
var itemToRemove = (from item in RemoteSystems
|
||||||
|
where item.Id == args.P0
|
||||||
|
select item).FirstOrDefault();
|
||||||
|
|
||||||
|
if (itemToRemove != null)
|
||||||
|
{
|
||||||
|
RemoteSystems.Remove(itemToRemove);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoteSystemAdded(RemoteSystemWatcher watcher, RemoteSystemAddedEventArgs args)
|
||||||
|
{
|
||||||
|
MainActivity.RunOnUiThread(delegate
|
||||||
|
{
|
||||||
|
RemoteSystems.Add(new RemoteSystemInfo
|
||||||
|
{
|
||||||
|
DisplayName = args.P0.DisplayName,
|
||||||
|
Id = args.P0.Id,
|
||||||
|
Kind = args.P0.Kind.ToString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "<Pending>")]
|
||||||
|
private void Platform_FetchAuthCode(string oauthUrl)
|
||||||
|
{
|
||||||
|
_authDialog = new Dialog(MainActivity);
|
||||||
|
|
||||||
|
var linearLayout = new LinearLayout(_authDialog.Context);
|
||||||
|
_webView = new WebView(_authDialog.Context);
|
||||||
|
linearLayout.AddView(_webView);
|
||||||
|
_authDialog.SetContentView(linearLayout);
|
||||||
|
|
||||||
|
_webView.SetWebChromeClient(new WebChromeClient());
|
||||||
|
_webView.Settings.JavaScriptEnabled = true;
|
||||||
|
_webView.Settings.DomStorageEnabled = true;
|
||||||
|
_webView.LoadUrl(oauthUrl);
|
||||||
|
|
||||||
|
_webView.SetWebViewClient(new MsaWebViewClient(MainActivity, _authDialog));
|
||||||
|
_authDialog.Show();
|
||||||
|
_authDialog.SetCancelable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose of the auth dialog and webview
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_authDialog != null)
|
||||||
|
{
|
||||||
|
_authDialog.Dispose();
|
||||||
|
_authDialog = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_webView != null)
|
||||||
|
{
|
||||||
|
_webView.Dispose();
|
||||||
|
_webView = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MsaWebViewClient : WebViewClient
|
||||||
|
{
|
||||||
|
bool authComplete;
|
||||||
|
|
||||||
|
private readonly MainActivity _parentActivity;
|
||||||
|
private readonly Dialog _authDialog;
|
||||||
|
|
||||||
|
public MsaWebViewClient(MainActivity activity, Dialog authDialog)
|
||||||
|
{
|
||||||
|
_parentActivity = activity;
|
||||||
|
_authDialog = authDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "<Pending>")]
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "<Pending>")]
|
||||||
|
public override void OnPageFinished(WebView view, string url)
|
||||||
|
{
|
||||||
|
base.OnPageFinished(view, url);
|
||||||
|
if (url.Contains("?code=", StringComparison.InvariantCultureIgnoreCase) && !authComplete)
|
||||||
|
{
|
||||||
|
authComplete = true;
|
||||||
|
|
||||||
|
var uri = Android.Net.Uri.Parse(url);
|
||||||
|
string token = uri.GetQueryParameter("code");
|
||||||
|
_authDialog.Dismiss();
|
||||||
|
Platform.SetAuthCode(token);
|
||||||
|
}
|
||||||
|
else if (url.Contains("error=access_denied", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
authComplete = true;
|
||||||
|
//Console.WriteLine("Page finished failed with ACCESS_DENIED_HERE");
|
||||||
|
Intent resultIntent = new Intent();
|
||||||
|
_parentActivity.SetResult(0, resultIntent);
|
||||||
|
_authDialog.Dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
Source/Sketch360.XPlat.Android/Resources/AndroidResources.Designer.cs
сгенерированный
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid.Resources {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
public class AndroidResources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal AndroidResources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
public static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Sketch360.XPlat.Droid.Resources.AndroidResources", typeof(AndroidResources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
public static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to OK.
|
||||||
|
/// </summary>
|
||||||
|
public static string OK {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("OK", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Sketch 360 Permissions.
|
||||||
|
/// </summary>
|
||||||
|
public static string PermissionsTitle {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PermissionsTitle", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to In Settings/Apps & notifications/Sketch 360/Permissions, select the Storage permission and check Allow to enable Sketch 360 to save files on this device, then try saving again..
|
||||||
|
/// </summary>
|
||||||
|
public static string PermssionsMessage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("PermssionsMessage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="OK" xml:space="preserve">
|
||||||
|
<value>OK</value>
|
||||||
|
</data>
|
||||||
|
<data name="PermissionsTitle" xml:space="preserve">
|
||||||
|
<value>Sketch 360 Permissions</value>
|
||||||
|
</data>
|
||||||
|
<data name="PermssionsMessage" xml:space="preserve">
|
||||||
|
<value>In Settings/Apps & notifications/Sketch 360/Permissions, select the Storage permission and check Allow to enable Sketch 360 to save files on this device, then try saving again.</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
После Ширина: | Высота: | Размер: 474 B |
После Ширина: | Высота: | Размер: 690 B |
После Ширина: | Высота: | Размер: 1.0 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/drawable/DrawingViewCarousel.png
Normal file
После Ширина: | Высота: | Размер: 1008 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/drawable/EquirectangularGrid.png
Normal file
После Ширина: | Высота: | Размер: 841 KiB |
После Ширина: | Высота: | Размер: 808 B |
После Ширина: | Высота: | Размер: 546 B |
После Ширина: | Высота: | Размер: 586 B |
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 226 B |
После Ширина: | Высота: | Размер: 993 B |
После Ширина: | Высота: | Размер: 741 B |
После Ширина: | Высота: | Размер: 907 B |
После Ширина: | Высота: | Размер: 780 B |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/drawable/SphericalViewCarousel.png
Normal file
После Ширина: | Высота: | Размер: 717 KiB |
После Ширина: | Высота: | Размер: 775 B |
После Ширина: | Высота: | Размер: 910 B |
После Ширина: | Высота: | Размер: 784 B |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/drawable/VerticalLinesButton.png
Normal file
После Ширина: | Высота: | Размер: 289 B |
После Ширина: | Высота: | Размер: 1.0 KiB |
После Ширина: | Высота: | Размер: 298 KiB |
|
@ -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,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>
|
После Ширина: | Высота: | Размер: 2.9 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/mipmap-hdpi/launcher_foreground.png
Normal file
После Ширина: | Высота: | Размер: 8.1 KiB |
После Ширина: | Высота: | Размер: 1.8 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/mipmap-mdpi/launcher_foreground.png
Normal file
После Ширина: | Высота: | Размер: 5.0 KiB |
После Ширина: | Высота: | Размер: 4.2 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/mipmap-xhdpi/launcher_foreground.png
Normal file
После Ширина: | Высота: | Размер: 12 KiB |
После Ширина: | Высота: | Размер: 6.8 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/mipmap-xxhdpi/launcher_foreground.png
Normal file
После Ширина: | Высота: | Размер: 8.4 KiB |
После Ширина: | Высота: | Размер: 10 KiB |
Двоичные данные
Source/Sketch360.XPlat.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png
Normal file
После Ширина: | Высота: | Размер: 13 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="launcher_background">#FFFFFF</color>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="MainTheme" parent="MainTheme.Base">
|
||||||
|
</style>
|
||||||
|
<!-- Base theme applied no matter what API -->
|
||||||
|
<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
|
<!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
<!--We will be using the toolbar so no need to show ActionBar-->
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<!-- Set theme colors from https://aka.ms/material-colors -->
|
||||||
|
<!-- colorPrimary is used for the default action bar background -->
|
||||||
|
<item name="colorPrimary">#2196F3</item>
|
||||||
|
<!-- colorPrimaryDark is used for the status bar -->
|
||||||
|
<item name="colorPrimaryDark">#1976D2</item>
|
||||||
|
<!-- colorAccent is used as the default value for colorControlActivated
|
||||||
|
which is used to tint widgets -->
|
||||||
|
<item name="colorAccent">#FF4081</item>
|
||||||
|
<!-- You can also set colorControlNormal, colorControlActivated
|
||||||
|
colorControlHighlight and colorSwitchThumbNormal. -->
|
||||||
|
<item name="windowActionModeOverlay">true</item>
|
||||||
|
|
||||||
|
<item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
|
||||||
|
<item name="colorAccent">#FF4081</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
|
@ -0,0 +1,271 @@
|
||||||
|
<?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>{319EA186-FA3E-49FB-9919-E48FBB2E35E6}</ProjectGuid>
|
||||||
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<TemplateGuid>{c9e5eea5-ca05-42a1-839b-61506e0a37df}</TemplateGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>Sketch360.XPlat.Droid</RootNamespace>
|
||||||
|
<AssemblyName>Sketch360.XPlat.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>v10.0</TargetFrameworkVersion>
|
||||||
|
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
|
||||||
|
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||||
|
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
<LangVersion>8.0</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidLinkMode>None</AndroidLinkMode>
|
||||||
|
<AndroidSupportedAbis />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||||
|
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||||
|
<AndroidSupportedAbis>armeabi-v7a;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||||
|
<AndroidKeyStore>false</AndroidKeyStore>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Mono.Android" />
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
<Reference Include="System.Numerics.Vectors" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AppCenter.Analytics">
|
||||||
|
<Version>3.4.3</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.AppCenter.Crashes">
|
||||||
|
<Version>3.4.3</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers">
|
||||||
|
<Version>3.3.1</Version>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.ConnectedDevices.Xamarin.Droid">
|
||||||
|
<Version>0.8.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="SkiaSharp">
|
||||||
|
<Version>2.80.2</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Xamarin.Android.Support.Compat">
|
||||||
|
<Version>28.0.0.3</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Xamarin.DuoSdk">
|
||||||
|
<Version>0.0.3.4</Version>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Xamarin.Forms" Version="4.8.0.1560" />
|
||||||
|
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="28.0.0.3" />
|
||||||
|
<PackageReference Include="Xamarin.Essentials" Version="1.5.3-preview1304" />
|
||||||
|
<PackageReference Include="Xamarin.Forms.DualScreen">
|
||||||
|
<Version>4.8.0.1560</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="BaseUrl.cs" />
|
||||||
|
<Compile Include="MainActivity.cs" />
|
||||||
|
<Compile Include="PhotoLibrary.cs" />
|
||||||
|
<Compile Include="Remote.cs" />
|
||||||
|
<Compile Include="Resources\Resource.designer.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="CustomWebViewRenderer.cs" />
|
||||||
|
<Compile Include="Resources\AndroidResources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>AndroidResources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Properties\AndroidManifest.xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\layout\Tabbar.xml" />
|
||||||
|
<AndroidResource Include="Resources\layout\Toolbar.xml" />
|
||||||
|
<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" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\EraserButton.png">
|
||||||
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
</AndroidResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Sketch360.XPlat\Sketch360.XPlat.csproj">
|
||||||
|
<Project>{DA1D6F34-3FD1-4842-8FBF-D73F820D04FC}</Project>
|
||||||
|
<Name>Sketch360.XPlat</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\babylon.gui.js">
|
||||||
|
<Link>Assets\babylon.gui.js</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\spherical.css">
|
||||||
|
<Link>Assets\spherical.css</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\spherical.gui.js">
|
||||||
|
<Link>Assets\spherical.gui.js</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\spherical.html">
|
||||||
|
<Link>Assets\spherical.html</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\spherical.js">
|
||||||
|
<Link>Assets\spherical.js</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\TiltRotate.png">
|
||||||
|
<Link>Assets\TiltRotate.png</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\EquirectangularGrid.png" />
|
||||||
|
<EmbeddedResource Include="Resources\AndroidResources.resx">
|
||||||
|
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>AndroidResources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\DrawButton.png">
|
||||||
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
</AndroidResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\RedoButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\UndoButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\PaletteButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\MenuButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\DrawingViewButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\View360Button.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\PenSizeButton.png">
|
||||||
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
</AndroidResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\PanZoomButton.png">
|
||||||
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
</AndroidResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\FrontBackButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\LeftRightButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\VerticalLinesButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\StencilButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\logo.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\babylon.4.1.gui.js">
|
||||||
|
<Link>Assets\babylon.4.1.gui.js</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
<AndroidAsset Include="..\Sketch360.XPlat.UWP\html\babylon.4.1.js">
|
||||||
|
<Link>Assets\babylon.4.1.js</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\DrawingViewCarousel.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\SphericalViewCarousel.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\export360Degree.png" />
|
||||||
|
<AndroidResource Include="Resources\drawable\TouchDrawing.png">
|
||||||
|
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||||
|
</AndroidResource>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\about.html">
|
||||||
|
<Link>Assets\about.html</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\EquirectangularGrid.png">
|
||||||
|
<Link>Assets\EquirectangularGrid.png</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\garage.png">
|
||||||
|
<Link>Assets\garage.png</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\ui.png">
|
||||||
|
<Link>Assets\ui.png</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\about.css">
|
||||||
|
<Link>Assets\about.css</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\about.js">
|
||||||
|
<Link>Assets\about.js</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidAsset Include="..\grid.jpg">
|
||||||
|
<Link>Assets\grid.jpg</Link>
|
||||||
|
</AndroidAsset>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidResource Include="Resources\drawable\BackButton.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<UserProperties XamarinHotReloadDebuggerTimeoutExceptionSketch360XPlatAndroidHideInfoBar="True" />
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
</Project>
|
|
@ -0,0 +1,72 @@
|
||||||
|
using Android.Content;
|
||||||
|
using Android.Widget;
|
||||||
|
using Sketch360.XPlat;
|
||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Xamarin.Forms.Platform.Android;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(
|
||||||
|
typeof(ZoomableScrollView),
|
||||||
|
typeof(Sketch360.XPlat.Droid.ZoomableScrollViewerRenderer))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.Droid
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ZoomableScrollView UWP Renderer
|
||||||
|
/// </summary>
|
||||||
|
public class ZoomableScrollViewerRenderer : ScrollViewRenderer
|
||||||
|
{
|
||||||
|
public ZoomableScrollViewerRenderer(Context context) :
|
||||||
|
base(context)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#region Methods
|
||||||
|
/// <summary>
|
||||||
|
/// On element changed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">the element changed event arguments</param>
|
||||||
|
protected override void OnElementChanged(VisualElementChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e == null) throw new ArgumentNullException(nameof(e));
|
||||||
|
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if (e.OldElement != null)
|
||||||
|
{
|
||||||
|
e.OldElement.PropertyChanged -= NewElement_PropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.NewElement != null)
|
||||||
|
{
|
||||||
|
if (e.NewElement is IZoomableScrollView zoomable)
|
||||||
|
{
|
||||||
|
e.NewElement.PropertyChanged += NewElement_PropertyChanged;
|
||||||
|
|
||||||
|
UpdateZoomMode(zoomable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation
|
||||||
|
private void NewElement_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(IZoomableScrollView.IsZoomEnabled))
|
||||||
|
{
|
||||||
|
UpdateZoomMode(sender as IZoomableScrollView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateZoomMode(IZoomableScrollView zoomableScrollView)
|
||||||
|
{
|
||||||
|
if (zoomableScrollView.IsZoomEnabled)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
MONO_LOG_LEVEL=error
|
|
@ -0,0 +1,8 @@
|
||||||
|
<Application
|
||||||
|
x:Class="Sketch360.XPlat.UWP.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:Sketch360.XPlat.UWP"
|
||||||
|
RequestedTheme="Light">
|
||||||
|
|
||||||
|
</Application>
|
|
@ -0,0 +1,90 @@
|
||||||
|
using System;
|
||||||
|
using Windows.ApplicationModel;
|
||||||
|
using Windows.ApplicationModel.Activation;
|
||||||
|
using Windows.UI.Xaml;
|
||||||
|
using Windows.UI.Xaml.Controls;
|
||||||
|
using Windows.UI.Xaml.Navigation;
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.UWP
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides application-specific behavior to supplement the default Application class.
|
||||||
|
/// </summary>
|
||||||
|
sealed partial class App : Application
|
||||||
|
{
|
||||||
|
/// <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();
|
||||||
|
this.Suspending += OnSuspending;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||||
|
/// will be used such as when the application is launched to open a specific file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">Details about the launch request and process.</param>
|
||||||
|
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e == null) throw new ArgumentNullException(nameof(e));
|
||||||
|
|
||||||
|
|
||||||
|
// Do not repeat app initialization when the Window already has content,
|
||||||
|
// just ensure that the window is active
|
||||||
|
if (!(Window.Current.Content is Frame rootFrame))
|
||||||
|
{
|
||||||
|
// Create a Frame to act as the navigation context and navigate to the first page
|
||||||
|
rootFrame = new Frame();
|
||||||
|
|
||||||
|
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||||
|
|
||||||
|
Xamarin.Forms.Forms.Init(e);
|
||||||
|
|
||||||
|
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
|
||||||
|
{
|
||||||
|
//TODO: Load state from previously suspended application
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place the frame in the current Window
|
||||||
|
Window.Current.Content = rootFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootFrame.Content == null)
|
||||||
|
{
|
||||||
|
// When the navigation stack isn't restored navigate to the first page,
|
||||||
|
// configuring the new page by passing required information as a navigation
|
||||||
|
// parameter
|
||||||
|
rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||||
|
}
|
||||||
|
// Ensure the current window is active
|
||||||
|
Window.Current.Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when Navigation to a certain page fails
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The Frame which failed navigation</param>
|
||||||
|
/// <param name="e">Details about the navigation failure</param>
|
||||||
|
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when application execution is being suspended. Application state is saved
|
||||||
|
/// without knowing whether the application will be terminated or resumed with the contents
|
||||||
|
/// of memory still intact.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender">The source of the suspend request.</param>
|
||||||
|
/// <param name="e">Details about the suspend request.</param>
|
||||||
|
private void OnSuspending(object sender, SuspendingEventArgs e)
|
||||||
|
{
|
||||||
|
var deferral = e.SuspendingOperation.GetDeferral();
|
||||||
|
//TODO: Save application state and stop any background activity
|
||||||
|
deferral.Complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
После Ширина: | Высота: | Размер: 6.0 KiB |
После Ширина: | Высота: | Размер: 14 KiB |
После Ширина: | Высота: | Размер: 31 KiB |
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 2.5 KiB |
После Ширина: | Высота: | Размер: 5.4 KiB |
После Ширина: | Высота: | Размер: 6.4 KiB |
После Ширина: | Высота: | Размер: 15 KiB |
После Ширина: | Высота: | Размер: 39 KiB |
После Ширина: | Высота: | Размер: 2.7 KiB |
После Ширина: | Высота: | Размер: 5.8 KiB |
После Ширина: | Высота: | Размер: 13 KiB |
Двоичные данные
Source/Sketch360.XPlat.UWP/Assets/Square44x44Logo.altform-unplated_targetsize-16.png
Normal file
После Ширина: | Высота: | Размер: 394 B |
Двоичные данные
Source/Sketch360.XPlat.UWP/Assets/Square44x44Logo.altform-unplated_targetsize-256.png
Normal file
После Ширина: | Высота: | Размер: 9.5 KiB |
Двоичные данные
Source/Sketch360.XPlat.UWP/Assets/Square44x44Logo.altform-unplated_targetsize-48.png
Normal file
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 1.1 KiB |
После Ширина: | Высота: | Размер: 2.4 KiB |
После Ширина: | Высота: | Размер: 4.6 KiB |
После Ширина: | Высота: | Размер: 394 B |
После Ширина: | Высота: | Размер: 9.5 KiB |
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 392 B |
После Ширина: | Высота: | Размер: 836 B |
После Ширина: | Высота: | Размер: 1.7 KiB |
После Ширина: | Высота: | Размер: 3.6 KiB |
После Ширина: | Высота: | Размер: 2.9 KiB |
После Ширина: | Высота: | Размер: 6.4 KiB |
После Ширина: | Высота: | Размер: 15 KiB |
|
@ -0,0 +1,32 @@
|
||||||
|
using System.IO;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
[assembly: Dependency(typeof(Sketch360.XPlat.UWP.BaseUrl))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.UWP
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UWP Base URL
|
||||||
|
/// </summary>
|
||||||
|
public class BaseUrl : IBaseUrl
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an appx package url scheme
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>an appx url scheme</returns>
|
||||||
|
public string GetBase()
|
||||||
|
{
|
||||||
|
return "ms-appx-web:///html/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the drawable image stream for UWP.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">the filename</param>
|
||||||
|
/// <returns>a image stream</returns>
|
||||||
|
public Stream GetDrawableImageStream(string filename)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
После Ширина: | Высота: | Размер: 841 KiB |
|
@ -0,0 +1,71 @@
|
||||||
|
using System;
|
||||||
|
using Xamarin.Forms.Inking.Views;
|
||||||
|
using Xamarin.Forms.Platform.UWP;
|
||||||
|
|
||||||
|
[assembly: ExportRenderer(
|
||||||
|
typeof(InkCanvasView),
|
||||||
|
typeof(Sketch360.XPlat.UWP.InkCanvasViewRenderer))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.UWP
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UWP InkCanvasView renderer using the native UWP InkCanvas
|
||||||
|
/// </summary>
|
||||||
|
public class InkCanvasViewRenderer : ViewRenderer<InkCanvasView, ScrollingInkCanvas>
|
||||||
|
{
|
||||||
|
#region Methods
|
||||||
|
/// <summary>
|
||||||
|
/// Create the UWP InkCanvas
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="e">the element changed event arguments</param>
|
||||||
|
protected override void OnElementChanged(ElementChangedEventArgs<InkCanvasView> e)
|
||||||
|
{
|
||||||
|
if (e == null) throw new ArgumentNullException(nameof(e));
|
||||||
|
|
||||||
|
base.OnElementChanged(e);
|
||||||
|
|
||||||
|
if (e.OldElement != null)
|
||||||
|
{
|
||||||
|
Control.InkCanvas.InkPresenter.StrokesCollected -= InkPresenter_StrokesCollected;
|
||||||
|
Control.InkCanvas.InkPresenter.StrokesErased -= InkPresenter_StrokesErased;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.NewElement != null)
|
||||||
|
{
|
||||||
|
if (Control == null)
|
||||||
|
{
|
||||||
|
var scrollingInkCanvas = new ScrollingInkCanvas();
|
||||||
|
|
||||||
|
scrollingInkCanvas.InkCanvas.InkPresenter.InputDeviceTypes = Windows.UI.Core.CoreInputDeviceTypes.Pen | Windows.UI.Core.CoreInputDeviceTypes.Touch | Windows.UI.Core.CoreInputDeviceTypes.Mouse;
|
||||||
|
scrollingInkCanvas.InkCanvas.InkPresenter.StrokesCollected += InkPresenter_StrokesCollected;
|
||||||
|
scrollingInkCanvas.InkCanvas.InkPresenter.StrokesErased += InkPresenter_StrokesErased;
|
||||||
|
|
||||||
|
SetNativeControl(scrollingInkCanvas);
|
||||||
|
|
||||||
|
if (e.NewElement.InkPresenter is UWPInkPresenter uwp)
|
||||||
|
{
|
||||||
|
uwp.InkCanvas = scrollingInkCanvas.InkCanvas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation
|
||||||
|
|
||||||
|
private void InkPresenter_StrokesErased(Windows.UI.Input.Inking.InkPresenter sender, Windows.UI.Input.Inking.InkStrokesErasedEventArgs args)
|
||||||
|
{
|
||||||
|
var strokes = args.Strokes.ToXInkStrokeList();
|
||||||
|
|
||||||
|
Element.InkPresenter.TriggerStrokesErased(strokes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InkPresenter_StrokesCollected(Windows.UI.Input.Inking.InkPresenter sender, Windows.UI.Input.Inking.InkStrokesCollectedEventArgs args)
|
||||||
|
{
|
||||||
|
var strokes = args.Strokes.ToXInkStrokeList();
|
||||||
|
|
||||||
|
Element.InkPresenter.TriggerStrokesCollected(strokes);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<forms:WindowsPage
|
||||||
|
x:Class="Sketch360.XPlat.UWP.MainPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:forms="using:Xamarin.Forms.Platform.UWP"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:Sketch360.XPlat.UWP"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
|
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</forms:WindowsPage>
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Sketch360.XPlat.UWP
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Xamarin Forms MainPage class
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class MainPage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the MainPage class.
|
||||||
|
/// </summary>
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "<Pending>")]
|
||||||
|
public MainPage()
|
||||||
|
{
|
||||||
|
this.InitializeComponent();
|
||||||
|
|
||||||
|
var app = new Sketch360.XPlat.App();
|
||||||
|
|
||||||
|
LoadApplication(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<Package
|
||||||
|
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||||
|
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||||
|
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||||
|
IgnorableNamespaces="uap mp">
|
||||||
|
|
||||||
|
<Identity
|
||||||
|
Name="75c5f008-7954-47cd-8922-030290a51106"
|
||||||
|
Publisher="CN=110fce08-04b0-4755-a169-63d8e7b7b04b"
|
||||||
|
Version="1.0.0.0" />
|
||||||
|
|
||||||
|
<mp:PhoneIdentity PhoneProductId="ec0cc741-fd3e-485c-81be-68815c480690" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||||
|
|
||||||
|
<Properties>
|
||||||
|
<DisplayName>Sketch360.XPlat.UWP</DisplayName>
|
||||||
|
<PublisherDisplayName>110fce08-04b0-4755-a169-63d8e7b7b04b</PublisherDisplayName>
|
||||||
|
<Logo>Assets\StoreLogo.png</Logo>
|
||||||
|
</Properties>
|
||||||
|
|
||||||
|
<Dependencies>
|
||||||
|
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||||
|
</Dependencies>
|
||||||
|
|
||||||
|
<Resources>
|
||||||
|
<Resource Language="x-generate"/>
|
||||||
|
</Resources>
|
||||||
|
|
||||||
|
<Applications>
|
||||||
|
<Application Id="App"
|
||||||
|
Executable="$targetnametoken$.exe"
|
||||||
|
EntryPoint="Sketch360.XPlat.UWP.App">
|
||||||
|
<uap:VisualElements
|
||||||
|
DisplayName="Sketch 360"
|
||||||
|
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||||
|
Square44x44Logo="Assets\Square44x44Logo.png"
|
||||||
|
Description="360 Sketching app"
|
||||||
|
BackgroundColor="transparent">
|
||||||
|
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square310x310Logo="Assets\LargeTile.png" Square71x71Logo="Assets\SmallTile.png">
|
||||||
|
<uap:ShowNameOnTiles>
|
||||||
|
<uap:ShowOn Tile="square150x150Logo" />
|
||||||
|
<uap:ShowOn Tile="wide310x150Logo" />
|
||||||
|
<uap:ShowOn Tile="square310x310Logo" />
|
||||||
|
</uap:ShowNameOnTiles>
|
||||||
|
</uap:DefaultTile>
|
||||||
|
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||||
|
</uap:VisualElements>
|
||||||
|
</Application>
|
||||||
|
</Applications>
|
||||||
|
|
||||||
|
<Capabilities>
|
||||||
|
<Capability Name="internetClient" />
|
||||||
|
</Capabilities>
|
||||||
|
</Package>
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Resources;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Sketch 360")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Microsoft Garage")]
|
||||||
|
[assembly: AssemblyProduct("Sketch360.XPlat.UWP")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Microsoft Copr. 2020")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
[assembly: NeutralResourcesLanguage("en-US")]
|
|
@ -0,0 +1,31 @@
|
||||||
|
<!--
|
||||||
|
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
|
||||||
|
developers. However, you can modify these parameters to modify the behavior of the .NET Native
|
||||||
|
optimizer.
|
||||||
|
|
||||||
|
Runtime Directives are documented at https://go.microsoft.com/fwlink/?LinkID=391919
|
||||||
|
|
||||||
|
To fully enable reflection for App1.MyClass and all of its public/private members
|
||||||
|
<Type Name="App1.MyClass" Dynamic="Required All"/>
|
||||||
|
|
||||||
|
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
|
||||||
|
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
|
||||||
|
|
||||||
|
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
|
||||||
|
<Namespace Name="DataClasses.ViewModels" Seralize="All" />
|
||||||
|
-->
|
||||||
|
|
||||||
|
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||||
|
<Application>
|
||||||
|
<!--
|
||||||
|
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||||
|
the application package. The asterisks are not wildcards.
|
||||||
|
-->
|
||||||
|
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Add your application specific runtime directives here. -->
|
||||||
|
|
||||||
|
|
||||||
|
</Application>
|
||||||
|
</Directives>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<UserControl
|
||||||
|
x:Class="Sketch360.XPlat.UWP.ScrollingInkCanvas"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="using:Sketch360.XPlat.UWP"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300"
|
||||||
|
d:DesignWidth="400">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<Style TargetType="ScrollViewer" x:Key="InkScrollViewerStyle">
|
||||||
|
<Setter Property="ZoomMode" Value="Enabled"/>
|
||||||
|
<Setter Property="HorizontalScrollMode" Value="Enabled"/>
|
||||||
|
<Setter Property="VerticalScrollMode" Value="Enabled"/>
|
||||||
|
<Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
|
||||||
|
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="InkCanvas" x:Key="InkCanvasStyle">
|
||||||
|
<Setter Property="Width" Value="2000"/>
|
||||||
|
<Setter Property="Height" Value="1000"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<ScrollViewer Style="{StaticResource InkScrollViewerStyle}">
|
||||||
|
<Grid>
|
||||||
|
<InkCanvas x:Name="InkCanvasControl" Style="{StaticResource InkCanvasStyle}"/>
|
||||||
|
</Grid>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
|
@ -0,0 +1,23 @@
|
||||||
|
using Windows.UI.Xaml.Controls;
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.UWP
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Scrolling ink canvas
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class ScrollingInkCanvas : UserControl
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the ScrollingInkCanvas class.
|
||||||
|
/// </summary>
|
||||||
|
public ScrollingInkCanvas()
|
||||||
|
{
|
||||||
|
this.InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ink canvas
|
||||||
|
/// </summary>
|
||||||
|
public InkCanvas InkCanvas => InkCanvasControl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Xamarin.Duo.Forms.Samples;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
[assembly: Dependency(typeof(Sketch360.XPlat.UWP.Services.HingeService))]
|
||||||
|
|
||||||
|
namespace Sketch360.XPlat.UWP.Services
|
||||||
|
{
|
||||||
|
public sealed class HingeService : IHingeService
|
||||||
|
{
|
||||||
|
public bool IsSpanned => false;
|
||||||
|
|
||||||
|
public bool IsLandscape => true;
|
||||||
|
|
||||||
|
public event EventHandler<HingeEventArgs> OnHingeUpdated;
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle GetHinge()
|
||||||
|
{
|
||||||
|
return Rectangle.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|