├── .gitignore
├── LICENSE
├── README.md
├── media
├── N1QL_Rick.gif
├── add_bucket.png
├── add_bucket_dialog.png
├── add_user.png
├── add_user_dialog.png
├── buckets.png
├── create_primary_index.png
├── indexes.png
├── query.png
├── security.png
└── snake.gif
└── src
├── basic-sync-function.json
├── ios
└── CouchDraw
│ ├── CouchDraw.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── roberthedgpeth.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── roberthedgpeth.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── CouchDraw
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── ViewController.swift
└── xamarin
├── CouchDraw.Android
├── Assets
│ └── AboutAssets.txt
├── CouchDraw.Android.csproj
├── Effects
│ └── TouchEffect.cs
├── MainActivity.cs
├── Properties
│ ├── AndroidManifest.xml
│ └── AssemblyInfo.cs
└── Resources
│ ├── AboutResources.txt
│ ├── Resource.designer.cs
│ ├── layout
│ ├── Tabbar.axml
│ └── Toolbar.axml
│ ├── mipmap-anydpi-v26
│ ├── icon.xml
│ └── icon_round.xml
│ ├── mipmap-hdpi
│ ├── icon.png
│ └── launcher_foreground.png
│ ├── mipmap-mdpi
│ ├── icon.png
│ └── launcher_foreground.png
│ ├── mipmap-xhdpi
│ ├── icon.png
│ └── launcher_foreground.png
│ ├── mipmap-xxhdpi
│ ├── icon.png
│ └── launcher_foreground.png
│ ├── mipmap-xxxhdpi
│ ├── icon.png
│ └── launcher_foreground.png
│ └── values
│ ├── colors.xml
│ └── styles.xml
├── CouchDraw.Core
├── AppInstance.cs
├── CouchDraw.Core.csproj
├── Repositories
│ └── ICanvasRepository.cs
└── ViewModels
│ └── MainViewModel.cs
├── CouchDraw.Models
├── CouchDraw.Models.csproj
├── Path.cs
└── Point.cs
├── CouchDraw.Repositories
├── CanvasRepository.cs
├── CouchDraw.Repositories.csproj
└── DatabaseManager.cs
├── CouchDraw.iOS
├── AppDelegate.cs
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon1024.png
│ │ ├── Icon120.png
│ │ ├── Icon152.png
│ │ ├── Icon167.png
│ │ ├── Icon180.png
│ │ ├── Icon20.png
│ │ ├── Icon29.png
│ │ ├── Icon40.png
│ │ ├── Icon58.png
│ │ ├── Icon60.png
│ │ ├── Icon76.png
│ │ ├── Icon80.png
│ │ └── Icon87.png
├── CouchDraw.iOS.csproj
├── Effects
│ ├── TouchEffect.cs
│ └── TouchRecognizer.cs
├── Entitlements.plist
├── Info.plist
├── Main.cs
├── Properties
│ └── AssemblyInfo.cs
└── Resources
│ ├── Default-568h@2x.png
│ ├── Default-Portrait.png
│ ├── Default-Portrait@2x.png
│ ├── Default.png
│ ├── Default@2x.png
│ └── LaunchScreen.storyboard
├── CouchDraw.sln
└── CouchDraw
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── CouchDraw.csproj
├── MainPage.xaml
├── MainPage.xaml.cs
└── TouchTracking
├── TouchActionEventArgs.cs
├── TouchActionEventHandler.cs
├── TouchActionType.cs
└── TouchEffect.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CouchDraw
2 |
3 | **CouchDraw** combines the power of [SkiaSharp](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/), [Xamarin](https://visualstudio.microsoft.com/xamarin/), and [Couchbase](https://www.couchbase.com/) to create a fully synchronized canvas across devices and platforms. This readme will walk you through the steps for getting this app up and running (locally) in under 15 minutes!
4 |
5 |
6 |
7 |
8 |
9 | # Table of Contents
10 | 1. [Overview](#overview)
11 | 2. [Requirements](#requirements)
12 | 3. [Getting Started](#getting-started)
13 | 1. [Mobile](#installation-mobile)
14 | 2. [Sync Gateway](#installation-sg)
15 | 3. [Couchbase Server](#installation-cbs)
16 | 5. [Configuring Couchbase Server for Sync Gateway](#configure-cbs)
17 | 1. [Create a bucket](#create-bucket)
18 | 2. [Create a user](#create-user)
19 | 6. [Starting Sync Gateway](#start-sg)
20 | 7. [Mobile Solution Architecture](#mobile-architecture)
21 | 8. [Running the solution](#running)
22 | 9. [Support and Contribution](#support-contribute)
23 |
24 | ## Overview
25 |
26 | CouchDraw is a simple synchronized drawing app powered by:
27 |
28 | * An embedded [Couchbase Lite](https://www.couchbase.com/products/lite) database.
29 | * The synchronization engine of [Sync Gateway](https://www.couchbase.com/products/sync-gateway)
30 | * A distributed [Couchbase Server](https://www.couchbase.com/products/server) database.
31 | * The cross-platform mobile solution capabilities of [Xamarin](https://visualstudio.microsoft.com/xamarin/).
32 |
33 | ## Requirements
34 |
35 | This project assumes familiarity with building Xamarin, more specifically [Xamarin.Forms](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/), apps using C# and XAML.
36 |
37 | * iOS (Xcode 9+)
38 | * Android (SDK 21+)
39 | * git (Optional) - this is required if you would prefer to pull the source code from GitHub repo.
40 | - Create a [free github account](https://github.com/) if you don’t already have one
41 | - git can be downloaded from git-scm.org
42 |
43 | ## Getting Started
44 |
45 | CouchDraw is a made of three parts:
46 |
47 | * A [Xamarin.Forms](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/) cross-platform mobile solution.
48 | * [Couchbase Sync Gateway](https://www.couchbase.com/products/sync-gateway) that facilitates the data synchronization between app instances.
49 | * [Couchbase Server](https://www.couchbase.com/products/server) which functions as the distributed database that houses CouchDraw's canvas data.
50 |
51 | Before jumping into the details here's TLDR; steps for getting this app up and running:
52 |
53 | 1. Pull down this repository.
54 | 2. [Install Sync Gateway](#installation-sg)
55 | 3. [Install Couchbase Server](#installation-cbs)
56 | 4. [Configure Couchbase Server](#configure-cbs)
57 | 5. [Start Sync Gateway](#start-sg)
58 | 6. [Build and run the app](#running)
59 |
60 | ### Mobile
61 |
62 | After pulling down this repository no additional installation is needed to build the [solution file](src/xamarin/CouchDraw.sln).
63 |
64 | Within the solution there are several notable Nuget packages that have been referenced and will be installed upon opening the solution within an IDE (i.e. Visual Studio).
65 |
66 | * [Couchbase.Lite](https://www.nuget.org/packages/Couchbase.Lite/)
67 | * [Couchbase.Lite.Mapping](https://www.nuget.org/packages/Couchbase.Lite.mapping/)
68 | * [SkiaSharp](https://www.nuget.org/packages/SkiaSharp/)
69 | * [Robo.Mvvm](https://www.nuget.org/packages/Robo.Mvvm/)
70 |
71 | ### Couchbase Sync Gateway
72 |
73 | Sync Gateway is the synchronization server in a Couchbase Mobile deployment.
74 |
75 | Sync Gateway is designed to provide data synchronization for large-scale interactive web, mobile, and IoT applications. Commonly used features include:
76 |
77 | * User authentication, ensuring that only authorized users can connect to Sync Gateway (see user [authentication guide](https://docs.couchbase.com/sync-gateway/current/authentication.html)).
78 |
79 | * Access Control, guaranteeing that only relevant documents are synced. Sync Gateway accomplishes this by examining document and applying business logic to decide whether to assign the documents to channels. Access control and ensuring that only relevant documents are synced are achieved through the use of channels and the [Sync Function](https://docs.couchbase.com/sync-gateway/current/sync-function-api.html).
80 |
81 | #### Installation
82 |
83 | Download the latest Sync Gateway 2.x installer from [Downloads](https://www.couchbase.com/downloads#couchbase-mobile) page. Be sure to select the "Mobile" tab.
84 |
85 | #### Settings
86 |
87 | To learn more about how to modify the Sync Gateway configuration file please see the documentation [here](https://docs.couchbase.com/sync-gateway/current/config-properties.html).
88 |
89 | ### Couchbase Server
90 |
91 | Couchbase Server is an open source, distributed, NoSQL document-oriented engagement database. It exposes a fast key-value store with managed cache for sub-millisecond data operations, purpose-built indexers for fast queries and a powerful query engine for executing SQL-like queries.
92 |
93 | #### Installation
94 |
95 | To install Couchbase Server please follow the instructions [here](https://docs.couchbase.com/server/6.0/getting-started/start-here.html).
96 |
97 | **Note:** I advise installing Couchbase Server _manually_ to start. If you install using Docker you will need to ensure that both Sync Gateway and Couchbase Server are running on the same Docker Host, and then you'll need to configure accordingly. You can find more instructions using Docker [here](https://blog.couchbase.com/using-docker-with-couchbase-mobile/).
98 |
99 | #### Starting Couchbase Server
100 |
101 | Once Couchbase Server has been installed simply navigate to where it has been installed and start "Couchbase Server".
102 |
103 | To start Couchbase Server using Docker please see the documentation [here](https://docs.couchbase.com/server/6.0/getting-started/do-a-quick-install.html).
104 |
105 | #### Accessing Couchbase Server
106 |
107 | Couchbase Server can be accessed using
108 | * [CLI](https://docs.couchbase.com/server/6.0/cli/cli-intro.html)
109 | * [API](https://docs.couchbase.com/server/6.0/rest-api/rest-intro.html)
110 | * An [administration (web) portal](https://docs.couchbase.com/server/6.0/getting-started/look-at-the-results.html)
111 |
112 | ## Configuring Couchbase Server for Sync Gateway
113 |
114 | Once Couchbase Server has been started navigate to the admin portal at **http://localhost:8091**.
115 |
116 | **Note:** Because CouchDraw is a simple demo app you're only going to be using [one node within a single cluster](https://docs.couchbase.com/server/6.0/learn/architecture-overview.html).
117 |
118 | ### Create a bucket
119 |
120 | Couchbase uses [buckets](https://docs.couchbase.com/server/5.5/understanding-couchbase/buckets-memory-and-storage/buckets.html) to group collections of keys and values logically. Simply put, documents are stored in buckets, and you're going to need a bucket to store CouchDraw documents in.
121 |
122 | To create a bucket that can be accessed by Sync Gateway and the embedded Couchbase Lite database do the following:
123 |
124 | 1. Within the Couchbase Server admin portal click "Buckets", located on the left hand navigation menu.
125 |
126 |
127 |
128 | 2. Click "Add Bucket".
129 |
130 |
131 |
132 | 3. Fill in the details with the following.
133 |
134 |
135 |
136 | **Note:** The Sync Gateway JSON configuration file that is included in the repo currently has a username of "couchdraw_user" and a password of "password". The key here is that whatever is in the Sync Gateway configuration file needs to be the same as the user you create in Couchbase Server in order to allow application access.
137 |
138 | ### Create a user
139 |
140 | In order for Sync Gateway to access the "couchdraw" bucket we need to create a user for it to use with the appropriate permissions. To do that you'll need to create a new users with the following steps.
141 |
142 | 1. Click "Security", located on the left hand navigation menu.
143 |
144 |
145 |
146 | 2. Click "Add User"
147 |
148 |
149 |
150 | 3. Once the dialog is displayed, do the following:
151 |
152 | * Add a username.
153 | * Add a password.
154 | * Select "Read-Only Admin" access under the "Administration & Global Roles" section.
155 | * Select "Application Access" under the "couchdraw" section.
156 |
157 |
158 |
159 | **Note:** The Sync Gateway JSON configuration file that is included in the repo currently has a username of "**couchdraw_user**" and a password of "**password**". The key here is that whatever is in the Sync Gateway configuration file needs to be the same as the user you create in Couchbase Server in order to allow application access. \
160 |
161 | ## Starting Sync Gateway
162 |
163 | After Couchbase Server is running and configured for CouchDraw you'll need to start Sync Gateway. To start Sync Gateway execute the following command.
164 |
165 | ```bash
166 | ~/where-you-installed-sync-gateway/couchbase-sync-gateway/bin/sync_gateway ~/path/to/basic-sync-function.json
167 | ```
168 |
169 | **Note:** "basic-sync-function.json" is included within the repo [here](src/basic-sync-function.json).
170 |
171 | When Sync Gateway initially starts and accesses Couchbase Server it will create a variety of indexes on the "couchdraw" bucket.
172 |
173 |
174 |
175 | ## Mobile Solution Architecture
176 |
177 | As mentioned before, CouchDraw is a Xamarin.Forms solution. Now that you've pulled down the project you can open it in your favorite IDE. You'll notice that the solution contains six projects:
178 |
179 | 1. [**CouchDraw**](src/xamarin/CouchDraw/CouchDraw.csproj): The main Xamarin.Forms project.
180 | 2. [**CouchDraw.Core**](src/xamarin/CouchDraw.Core/CouchDraw.Core.csproj): A .NET Standard project containing ViewModels and Repository interfaces.
181 | 3. [**CouchDraw.Models**](src/xamarin/CouchDraw.Models/CouchDraw.Models.csproj): A .NET Standard project that containing domain models.
182 | 4. [**CouchDraw.Repositories**](src/xamarin/CouchDraw.Repositories/CouchDraw.Repositories.csproj): A .NET Standard project containing the repositories and database managers used for integrating with Couchbase Lite and Sync Gateway.
183 | 5. [**CouchDraw.iOS**](src/xamarin/CouchDraw.iOS/CouchDraw.iOS.csproj): The Xamarin.iOS platform project.
184 | 6. [**CouchDraw.Android**](src/xamarin/CouchDraw.Android/CouchDraw.Android.csproj): The Xamarin.Android platform project.
185 |
186 | ## Running the solution
187 |
188 | Now that Couchbase Server has been configured and Sync Gateway is running you're ready to build and run the app!
189 |
190 | By default the app is set up to run locally, but it can certainly be configured to point to external instance of Sync Gateway and Couchbase Server as well.
191 |
192 | The connection information for Sync Gateway resides in [DatabaseManager.cs](src/xamarin/CouchDraw.Repositories/DatabaseManager.cs).
193 |
194 | ```C#
195 | // Note: User 'localhost' or '127.0.0.1' when using a simulator
196 | readonly Uri _remoteSyncUrl = new Uri("ws://localhost:4984");
197 |
198 | // Note: Use '10.0.2.2' when using an emulator
199 | //readonly Uri _remoteSyncUrl = new Uri("ws://10.0.2.2:4984");
200 | ```
201 |
202 |
203 |
204 |
205 |
206 | ## Support and Contribution
207 |
208 | Thanks so much for taking a look at CouchDraw! This is a very simple example, and there's a lot of potential for customization. Please feel free to contribute to this project. Here are a couple of ideas for features to add:
209 |
210 | - Replacing the color buttons with a range slider for color selection.
211 | - Adding the ability to change the line thickness.
212 | - Adding the ability to erase lines that have been drawn.
213 |
214 | If you have any questions, comments, or would like to contribute to this projects please reach out to me directly at robert.hedgpeth@couchbase.com or on [Twitter](https://twitter.com/probablyrealrob).
215 |
--------------------------------------------------------------------------------
/media/N1QL_Rick.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/N1QL_Rick.gif
--------------------------------------------------------------------------------
/media/add_bucket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/add_bucket.png
--------------------------------------------------------------------------------
/media/add_bucket_dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/add_bucket_dialog.png
--------------------------------------------------------------------------------
/media/add_user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/add_user.png
--------------------------------------------------------------------------------
/media/add_user_dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/add_user_dialog.png
--------------------------------------------------------------------------------
/media/buckets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/buckets.png
--------------------------------------------------------------------------------
/media/create_primary_index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/create_primary_index.png
--------------------------------------------------------------------------------
/media/indexes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/indexes.png
--------------------------------------------------------------------------------
/media/query.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/query.png
--------------------------------------------------------------------------------
/media/security.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/security.png
--------------------------------------------------------------------------------
/media/snake.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/media/snake.gif
--------------------------------------------------------------------------------
/src/basic-sync-function.json:
--------------------------------------------------------------------------------
1 | {
2 | "logging": {
3 | "console": {
4 | "log_level": "debug",
5 | "log_keys": ["*"],
6 | "color_enabled": false
7 | },
8 | "logKeys": ["*"],
9 | "logLevel": "debug",
10 | "debug": {
11 | "enabled": true
12 | }
13 | },
14 | "databases": {
15 | "couchdraw": {
16 | "server": "http://localhost:8091",
17 | "bucket": "couchdraw",
18 | "username": "couchdraw_user",
19 | "password": "password",
20 | "num_index_replicas": 0,
21 | "enable_shared_bucket_access": true,
22 | "import_docs": "continuous",
23 | "users": { "GUEST": { "disabled": false, "admin_channels": ["*"] } },
24 | "allow_conflicts": false,
25 | "revs_limit": 20
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C2C1BE2B22F8EA3D00D8C76A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2C1BE2A22F8EA3D00D8C76A /* AppDelegate.swift */; };
11 | C2C1BE2D22F8EA3D00D8C76A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2C1BE2C22F8EA3D00D8C76A /* ViewController.swift */; };
12 | C2C1BE3022F8EA3D00D8C76A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C2C1BE2E22F8EA3D00D8C76A /* Main.storyboard */; };
13 | C2C1BE3222F8EA4000D8C76A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C2C1BE3122F8EA4000D8C76A /* Assets.xcassets */; };
14 | C2C1BE3522F8EA4000D8C76A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C2C1BE3322F8EA4000D8C76A /* LaunchScreen.storyboard */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXFileReference section */
18 | C2C1BE2722F8EA3D00D8C76A /* CouchDraw.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CouchDraw.app; sourceTree = BUILT_PRODUCTS_DIR; };
19 | C2C1BE2A22F8EA3D00D8C76A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
20 | C2C1BE2C22F8EA3D00D8C76A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
21 | C2C1BE2F22F8EA3D00D8C76A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
22 | C2C1BE3122F8EA4000D8C76A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
23 | C2C1BE3422F8EA4000D8C76A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
24 | C2C1BE3622F8EA4000D8C76A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
25 | /* End PBXFileReference section */
26 |
27 | /* Begin PBXFrameworksBuildPhase section */
28 | C2C1BE2422F8EA3D00D8C76A /* Frameworks */ = {
29 | isa = PBXFrameworksBuildPhase;
30 | buildActionMask = 2147483647;
31 | files = (
32 | );
33 | runOnlyForDeploymentPostprocessing = 0;
34 | };
35 | /* End PBXFrameworksBuildPhase section */
36 |
37 | /* Begin PBXGroup section */
38 | C2C1BE1E22F8EA3D00D8C76A = {
39 | isa = PBXGroup;
40 | children = (
41 | C2C1BE2922F8EA3D00D8C76A /* CouchDraw */,
42 | C2C1BE2822F8EA3D00D8C76A /* Products */,
43 | );
44 | sourceTree = "";
45 | };
46 | C2C1BE2822F8EA3D00D8C76A /* Products */ = {
47 | isa = PBXGroup;
48 | children = (
49 | C2C1BE2722F8EA3D00D8C76A /* CouchDraw.app */,
50 | );
51 | name = Products;
52 | sourceTree = "";
53 | };
54 | C2C1BE2922F8EA3D00D8C76A /* CouchDraw */ = {
55 | isa = PBXGroup;
56 | children = (
57 | C2C1BE2A22F8EA3D00D8C76A /* AppDelegate.swift */,
58 | C2C1BE2C22F8EA3D00D8C76A /* ViewController.swift */,
59 | C2C1BE2E22F8EA3D00D8C76A /* Main.storyboard */,
60 | C2C1BE3122F8EA4000D8C76A /* Assets.xcassets */,
61 | C2C1BE3322F8EA4000D8C76A /* LaunchScreen.storyboard */,
62 | C2C1BE3622F8EA4000D8C76A /* Info.plist */,
63 | );
64 | path = CouchDraw;
65 | sourceTree = "";
66 | };
67 | /* End PBXGroup section */
68 |
69 | /* Begin PBXNativeTarget section */
70 | C2C1BE2622F8EA3D00D8C76A /* CouchDraw */ = {
71 | isa = PBXNativeTarget;
72 | buildConfigurationList = C2C1BE3922F8EA4000D8C76A /* Build configuration list for PBXNativeTarget "CouchDraw" */;
73 | buildPhases = (
74 | C2C1BE2322F8EA3D00D8C76A /* Sources */,
75 | C2C1BE2422F8EA3D00D8C76A /* Frameworks */,
76 | C2C1BE2522F8EA3D00D8C76A /* Resources */,
77 | );
78 | buildRules = (
79 | );
80 | dependencies = (
81 | );
82 | name = CouchDraw;
83 | productName = CouchDraw;
84 | productReference = C2C1BE2722F8EA3D00D8C76A /* CouchDraw.app */;
85 | productType = "com.apple.product-type.application";
86 | };
87 | /* End PBXNativeTarget section */
88 |
89 | /* Begin PBXProject section */
90 | C2C1BE1F22F8EA3D00D8C76A /* Project object */ = {
91 | isa = PBXProject;
92 | attributes = {
93 | LastSwiftUpdateCheck = 1020;
94 | LastUpgradeCheck = 1020;
95 | ORGANIZATIONNAME = "Robert Hedgpeth";
96 | TargetAttributes = {
97 | C2C1BE2622F8EA3D00D8C76A = {
98 | CreatedOnToolsVersion = 10.2.1;
99 | };
100 | };
101 | };
102 | buildConfigurationList = C2C1BE2222F8EA3D00D8C76A /* Build configuration list for PBXProject "CouchDraw" */;
103 | compatibilityVersion = "Xcode 9.3";
104 | developmentRegion = en;
105 | hasScannedForEncodings = 0;
106 | knownRegions = (
107 | en,
108 | Base,
109 | );
110 | mainGroup = C2C1BE1E22F8EA3D00D8C76A;
111 | productRefGroup = C2C1BE2822F8EA3D00D8C76A /* Products */;
112 | projectDirPath = "";
113 | projectRoot = "";
114 | targets = (
115 | C2C1BE2622F8EA3D00D8C76A /* CouchDraw */,
116 | );
117 | };
118 | /* End PBXProject section */
119 |
120 | /* Begin PBXResourcesBuildPhase section */
121 | C2C1BE2522F8EA3D00D8C76A /* Resources */ = {
122 | isa = PBXResourcesBuildPhase;
123 | buildActionMask = 2147483647;
124 | files = (
125 | C2C1BE3522F8EA4000D8C76A /* LaunchScreen.storyboard in Resources */,
126 | C2C1BE3222F8EA4000D8C76A /* Assets.xcassets in Resources */,
127 | C2C1BE3022F8EA3D00D8C76A /* Main.storyboard in Resources */,
128 | );
129 | runOnlyForDeploymentPostprocessing = 0;
130 | };
131 | /* End PBXResourcesBuildPhase section */
132 |
133 | /* Begin PBXSourcesBuildPhase section */
134 | C2C1BE2322F8EA3D00D8C76A /* Sources */ = {
135 | isa = PBXSourcesBuildPhase;
136 | buildActionMask = 2147483647;
137 | files = (
138 | C2C1BE2D22F8EA3D00D8C76A /* ViewController.swift in Sources */,
139 | C2C1BE2B22F8EA3D00D8C76A /* AppDelegate.swift in Sources */,
140 | );
141 | runOnlyForDeploymentPostprocessing = 0;
142 | };
143 | /* End PBXSourcesBuildPhase section */
144 |
145 | /* Begin PBXVariantGroup section */
146 | C2C1BE2E22F8EA3D00D8C76A /* Main.storyboard */ = {
147 | isa = PBXVariantGroup;
148 | children = (
149 | C2C1BE2F22F8EA3D00D8C76A /* Base */,
150 | );
151 | name = Main.storyboard;
152 | sourceTree = "";
153 | };
154 | C2C1BE3322F8EA4000D8C76A /* LaunchScreen.storyboard */ = {
155 | isa = PBXVariantGroup;
156 | children = (
157 | C2C1BE3422F8EA4000D8C76A /* Base */,
158 | );
159 | name = LaunchScreen.storyboard;
160 | sourceTree = "";
161 | };
162 | /* End PBXVariantGroup section */
163 |
164 | /* Begin XCBuildConfiguration section */
165 | C2C1BE3722F8EA4000D8C76A /* Debug */ = {
166 | isa = XCBuildConfiguration;
167 | buildSettings = {
168 | ALWAYS_SEARCH_USER_PATHS = NO;
169 | CLANG_ANALYZER_NONNULL = YES;
170 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
171 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
172 | CLANG_CXX_LIBRARY = "libc++";
173 | CLANG_ENABLE_MODULES = YES;
174 | CLANG_ENABLE_OBJC_ARC = YES;
175 | CLANG_ENABLE_OBJC_WEAK = YES;
176 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
177 | CLANG_WARN_BOOL_CONVERSION = YES;
178 | CLANG_WARN_COMMA = YES;
179 | CLANG_WARN_CONSTANT_CONVERSION = YES;
180 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
181 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
182 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
183 | CLANG_WARN_EMPTY_BODY = YES;
184 | CLANG_WARN_ENUM_CONVERSION = YES;
185 | CLANG_WARN_INFINITE_RECURSION = YES;
186 | CLANG_WARN_INT_CONVERSION = YES;
187 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
188 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
189 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
190 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
191 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
192 | CLANG_WARN_STRICT_PROTOTYPES = YES;
193 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
194 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
195 | CLANG_WARN_UNREACHABLE_CODE = YES;
196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
197 | CODE_SIGN_IDENTITY = "iPhone Developer";
198 | COPY_PHASE_STRIP = NO;
199 | DEBUG_INFORMATION_FORMAT = dwarf;
200 | ENABLE_STRICT_OBJC_MSGSEND = YES;
201 | ENABLE_TESTABILITY = YES;
202 | GCC_C_LANGUAGE_STANDARD = gnu11;
203 | GCC_DYNAMIC_NO_PIC = NO;
204 | GCC_NO_COMMON_BLOCKS = YES;
205 | GCC_OPTIMIZATION_LEVEL = 0;
206 | GCC_PREPROCESSOR_DEFINITIONS = (
207 | "DEBUG=1",
208 | "$(inherited)",
209 | );
210 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
211 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
212 | GCC_WARN_UNDECLARED_SELECTOR = YES;
213 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
214 | GCC_WARN_UNUSED_FUNCTION = YES;
215 | GCC_WARN_UNUSED_VARIABLE = YES;
216 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
217 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
218 | MTL_FAST_MATH = YES;
219 | ONLY_ACTIVE_ARCH = YES;
220 | SDKROOT = iphoneos;
221 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
222 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
223 | };
224 | name = Debug;
225 | };
226 | C2C1BE3822F8EA4000D8C76A /* Release */ = {
227 | isa = XCBuildConfiguration;
228 | buildSettings = {
229 | ALWAYS_SEARCH_USER_PATHS = NO;
230 | CLANG_ANALYZER_NONNULL = YES;
231 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
232 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
233 | CLANG_CXX_LIBRARY = "libc++";
234 | CLANG_ENABLE_MODULES = YES;
235 | CLANG_ENABLE_OBJC_ARC = YES;
236 | CLANG_ENABLE_OBJC_WEAK = YES;
237 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
238 | CLANG_WARN_BOOL_CONVERSION = YES;
239 | CLANG_WARN_COMMA = YES;
240 | CLANG_WARN_CONSTANT_CONVERSION = YES;
241 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
242 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
243 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
244 | CLANG_WARN_EMPTY_BODY = YES;
245 | CLANG_WARN_ENUM_CONVERSION = YES;
246 | CLANG_WARN_INFINITE_RECURSION = YES;
247 | CLANG_WARN_INT_CONVERSION = YES;
248 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
249 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
250 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
252 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
253 | CLANG_WARN_STRICT_PROTOTYPES = YES;
254 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
255 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
256 | CLANG_WARN_UNREACHABLE_CODE = YES;
257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
258 | CODE_SIGN_IDENTITY = "iPhone Developer";
259 | COPY_PHASE_STRIP = NO;
260 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
261 | ENABLE_NS_ASSERTIONS = NO;
262 | ENABLE_STRICT_OBJC_MSGSEND = YES;
263 | GCC_C_LANGUAGE_STANDARD = gnu11;
264 | GCC_NO_COMMON_BLOCKS = YES;
265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
267 | GCC_WARN_UNDECLARED_SELECTOR = YES;
268 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
269 | GCC_WARN_UNUSED_FUNCTION = YES;
270 | GCC_WARN_UNUSED_VARIABLE = YES;
271 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
272 | MTL_ENABLE_DEBUG_INFO = NO;
273 | MTL_FAST_MATH = YES;
274 | SDKROOT = iphoneos;
275 | SWIFT_COMPILATION_MODE = wholemodule;
276 | SWIFT_OPTIMIZATION_LEVEL = "-O";
277 | VALIDATE_PRODUCT = YES;
278 | };
279 | name = Release;
280 | };
281 | C2C1BE3A22F8EA4000D8C76A /* Debug */ = {
282 | isa = XCBuildConfiguration;
283 | buildSettings = {
284 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
285 | CODE_SIGN_STYLE = Automatic;
286 | DEVELOPMENT_TEAM = YKN5XLR52K;
287 | INFOPLIST_FILE = CouchDraw/Info.plist;
288 | LD_RUNPATH_SEARCH_PATHS = (
289 | "$(inherited)",
290 | "@executable_path/Frameworks",
291 | );
292 | PRODUCT_BUNDLE_IDENTIFIER = com.couchbaselabs.CouchDraw;
293 | PRODUCT_NAME = "$(TARGET_NAME)";
294 | SWIFT_VERSION = 5.0;
295 | TARGETED_DEVICE_FAMILY = "1,2";
296 | };
297 | name = Debug;
298 | };
299 | C2C1BE3B22F8EA4000D8C76A /* Release */ = {
300 | isa = XCBuildConfiguration;
301 | buildSettings = {
302 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
303 | CODE_SIGN_STYLE = Automatic;
304 | DEVELOPMENT_TEAM = YKN5XLR52K;
305 | INFOPLIST_FILE = CouchDraw/Info.plist;
306 | LD_RUNPATH_SEARCH_PATHS = (
307 | "$(inherited)",
308 | "@executable_path/Frameworks",
309 | );
310 | PRODUCT_BUNDLE_IDENTIFIER = com.couchbaselabs.CouchDraw;
311 | PRODUCT_NAME = "$(TARGET_NAME)";
312 | SWIFT_VERSION = 5.0;
313 | TARGETED_DEVICE_FAMILY = "1,2";
314 | };
315 | name = Release;
316 | };
317 | /* End XCBuildConfiguration section */
318 |
319 | /* Begin XCConfigurationList section */
320 | C2C1BE2222F8EA3D00D8C76A /* Build configuration list for PBXProject "CouchDraw" */ = {
321 | isa = XCConfigurationList;
322 | buildConfigurations = (
323 | C2C1BE3722F8EA4000D8C76A /* Debug */,
324 | C2C1BE3822F8EA4000D8C76A /* Release */,
325 | );
326 | defaultConfigurationIsVisible = 0;
327 | defaultConfigurationName = Release;
328 | };
329 | C2C1BE3922F8EA4000D8C76A /* Build configuration list for PBXNativeTarget "CouchDraw" */ = {
330 | isa = XCConfigurationList;
331 | buildConfigurations = (
332 | C2C1BE3A22F8EA4000D8C76A /* Debug */,
333 | C2C1BE3B22F8EA4000D8C76A /* Release */,
334 | );
335 | defaultConfigurationIsVisible = 0;
336 | defaultConfigurationName = Release;
337 | };
338 | /* End XCConfigurationList section */
339 | };
340 | rootObject = C2C1BE1F22F8EA3D00D8C76A /* Project object */;
341 | }
342 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw.xcodeproj/project.xcworkspace/xcuserdata/roberthedgpeth.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/ios/CouchDraw/CouchDraw.xcodeproj/project.xcworkspace/xcuserdata/roberthedgpeth.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw.xcodeproj/xcuserdata/roberthedgpeth.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw.xcodeproj/xcuserdata/roberthedgpeth.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CouchDraw.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CouchDraw
4 | //
5 | // Created by Robert Hedgpeth on 8/5/19.
6 | // Copyright © 2019 Robert Hedgpeth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/src/ios/CouchDraw/CouchDraw/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CouchDraw
4 | //
5 | // Created by Robert Hedgpeth on 8/5/19.
6 | // Copyright © 2019 Robert Hedgpeth. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | var lastPoint = CGPoint.zero
14 | var color = UIColor.black
15 | var brushWidth: CGFloat = 6.0
16 | var opacity: CGFloat = 1.0
17 | var swiped = false
18 |
19 | @IBOutlet weak var tempImageView: UIImageView!
20 | @IBOutlet weak var mainImageView: UIImageView!
21 |
22 | override func viewDidLoad() {
23 | super.viewDidLoad()
24 | // Do any additional setup after loading the view.
25 | }
26 |
27 | @IBAction func colorButton1_Tapped(_ sender: Any) {
28 | color = UIColor.black
29 | }
30 |
31 | @IBAction func colorButton2_Tapped(_ sender: Any) {
32 | color = UIColor.red
33 | }
34 |
35 | @IBAction func colorButton3_Tapped(_ sender: Any) {
36 | color = UIColor.blue
37 | }
38 |
39 | @IBAction func colorButton4_Tapped(_ sender: Any) {
40 | color = UIColor.green
41 | }
42 |
43 | @IBAction func undo_Tapped(_ sender: Any) {
44 |
45 | }
46 |
47 | @IBAction func clearAll_Tapped(_ sender: Any) {
48 |
49 | }
50 |
51 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
52 | guard let touch = touches.first else {
53 | return
54 | }
55 | swiped = false
56 | lastPoint = touch.location(in: view)
57 | }
58 |
59 | func drawLine(from fromPoint: CGPoint, to toPoint: CGPoint) {
60 | // 1
61 | UIGraphicsBeginImageContext(view.frame.size)
62 |
63 | guard let context = UIGraphicsGetCurrentContext() else {
64 | return
65 | }
66 |
67 | tempImageView.image?.draw(in: view.bounds)
68 |
69 | // 2
70 | context.move(to: fromPoint)
71 | context.addLine(to: toPoint)
72 |
73 | // 3
74 | context.setLineCap(.round)
75 | context.setBlendMode(.normal)
76 | context.setLineWidth(brushWidth)
77 | context.setStrokeColor(color.cgColor)
78 |
79 | // 4
80 | context.strokePath()
81 |
82 | // 5
83 | tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
84 | tempImageView.alpha = opacity
85 |
86 | UIGraphicsEndImageContext()
87 | }
88 |
89 | override func touchesMoved(_ touches: Set, with event: UIEvent?) {
90 | guard let touch = touches.first else {
91 | return
92 | }
93 |
94 | // 6
95 | swiped = true
96 | let currentPoint = touch.location(in: view)
97 | drawLine(from: lastPoint, to: currentPoint)
98 |
99 | // 7
100 | lastPoint = currentPoint
101 | }
102 |
103 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
104 | if !swiped {
105 | // draw a single point
106 | drawLine(from: lastPoint, to: lastPoint)
107 | }
108 |
109 | // Merge tempImageView into mainImageView
110 | UIGraphicsBeginImageContext(mainImageView.frame.size)
111 | mainImageView.image?.draw(in: view.bounds, blendMode: .normal, alpha: 1.0)
112 | tempImageView?.image?.draw(in: view.bounds, blendMode: .normal, alpha: opacity)
113 | mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
114 | UIGraphicsEndImageContext()
115 |
116 | tempImageView.image = nil
117 | }
118 | }
119 |
120 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Assets/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories) and given a Build Action of "AndroidAsset".
3 |
4 | These files will be deployed with you package and will be accessible using Android's
5 | AssetManager, like this:
6 |
7 | public class ReadAsset : Activity
8 | {
9 | protected override void OnCreate (Bundle bundle)
10 | {
11 | base.OnCreate (bundle);
12 |
13 | InputStream input = Assets.Open ("my_asset.txt");
14 | }
15 | }
16 |
17 | Additionally, some Android functions will automatically load asset files:
18 |
19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
20 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/CouchDraw.Android.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {380A192B-9661-4A64-BD8A-22134FF53A09}
7 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
8 | {c9e5eea5-ca05-42a1-839b-61506e0a37df}
9 | Library
10 | CouchDraw.Droid
11 | CouchDraw.Android
12 | True
13 | Resources\Resource.designer.cs
14 | Resource
15 | Properties\AndroidManifest.xml
16 | Resources
17 | Assets
18 | v9.0
19 | true
20 | Xamarin.Android.Net.AndroidClientHandler
21 |
22 |
23 |
24 |
25 | true
26 | portable
27 | false
28 | bin\Debug
29 | DEBUG;
30 | prompt
31 | 4
32 | None
33 |
34 |
35 | true
36 | portable
37 | true
38 | bin\Release
39 | prompt
40 | 4
41 | true
42 | false
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 1.68.0
65 |
66 |
67 | 1.68.0
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}
110 | CouchDraw
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Effects/TouchEffect.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | using Xamarin.Forms;
6 | using Xamarin.Forms.Platform.Android;
7 |
8 | using Android.Views;
9 |
10 | [assembly: ResolutionGroupName("CouchDraw")]
11 | [assembly: ExportEffect(typeof(TouchTracking.Droid.TouchEffect), "TouchEffect")]
12 |
13 | namespace TouchTracking.Droid
14 | {
15 | public class TouchEffect : PlatformEffect
16 | {
17 | Android.Views.View view;
18 | Element formsElement;
19 | TouchTracking.TouchEffect libTouchEffect;
20 | bool capture;
21 | Func fromPixels;
22 | int[] twoIntArray = new int[2];
23 |
24 | static Dictionary viewDictionary =
25 | new Dictionary();
26 |
27 | static Dictionary idToEffectDictionary =
28 | new Dictionary();
29 |
30 | protected override void OnAttached()
31 | {
32 | // Get the Android View corresponding to the Element that the effect is attached to
33 | view = Control == null ? Container : Control;
34 |
35 | // Get access to the TouchEffect class in the .NET Standard library
36 | TouchTracking.TouchEffect touchEffect =
37 | (TouchTracking.TouchEffect)Element.Effects.
38 | FirstOrDefault(e => e is TouchTracking.TouchEffect);
39 |
40 | if (touchEffect != null && view != null)
41 | {
42 | viewDictionary.Add(view, this);
43 |
44 | formsElement = Element;
45 |
46 | libTouchEffect = touchEffect;
47 |
48 | // Save fromPixels function
49 | fromPixels = view.Context.FromPixels;
50 |
51 | // Set event handler on View
52 | view.Touch += OnTouch;
53 | }
54 | }
55 |
56 | protected override void OnDetached()
57 | {
58 | if (viewDictionary.ContainsKey(view))
59 | {
60 | viewDictionary.Remove(view);
61 | view.Touch -= OnTouch;
62 | }
63 | }
64 |
65 | void OnTouch(object sender, Android.Views.View.TouchEventArgs args)
66 | {
67 | // Two object common to all the events
68 | Android.Views.View senderView = sender as Android.Views.View;
69 | MotionEvent motionEvent = args.Event;
70 |
71 | // Get the pointer index
72 | int pointerIndex = motionEvent.ActionIndex;
73 |
74 | // Get the id that identifies a finger over the course of its progress
75 | int id = motionEvent.GetPointerId(pointerIndex);
76 |
77 |
78 | senderView.GetLocationOnScreen(twoIntArray);
79 | Point screenPointerCoords = new Point(twoIntArray[0] + motionEvent.GetX(pointerIndex),
80 | twoIntArray[1] + motionEvent.GetY(pointerIndex));
81 |
82 |
83 | // Use ActionMasked here rather than Action to reduce the number of possibilities
84 | switch (args.Event.ActionMasked)
85 | {
86 | case MotionEventActions.Down:
87 | case MotionEventActions.PointerDown:
88 | FireEvent(this, id, TouchActionType.Pressed, screenPointerCoords, true);
89 |
90 | idToEffectDictionary.Add(id, this);
91 |
92 | capture = libTouchEffect.Capture;
93 | break;
94 |
95 | case MotionEventActions.Move:
96 | // Multiple Move events are bundled, so handle them in a loop
97 | for (pointerIndex = 0; pointerIndex < motionEvent.PointerCount; pointerIndex++)
98 | {
99 | id = motionEvent.GetPointerId(pointerIndex);
100 |
101 | if (capture)
102 | {
103 | senderView.GetLocationOnScreen(twoIntArray);
104 |
105 | screenPointerCoords = new Point(twoIntArray[0] + motionEvent.GetX(pointerIndex),
106 | twoIntArray[1] + motionEvent.GetY(pointerIndex));
107 |
108 | FireEvent(this, id, TouchActionType.Moved, screenPointerCoords, true);
109 | }
110 | else
111 | {
112 | CheckForBoundaryHop(id, screenPointerCoords);
113 |
114 | if (idToEffectDictionary[id] != null)
115 | {
116 | FireEvent(idToEffectDictionary[id], id, TouchActionType.Moved, screenPointerCoords, true);
117 | }
118 | }
119 | }
120 | break;
121 |
122 | case MotionEventActions.Up:
123 | case MotionEventActions.Pointer1Up:
124 | if (capture)
125 | {
126 | FireEvent(this, id, TouchActionType.Released, screenPointerCoords, false);
127 | }
128 | else
129 | {
130 | CheckForBoundaryHop(id, screenPointerCoords);
131 |
132 | if (idToEffectDictionary[id] != null)
133 | {
134 | FireEvent(idToEffectDictionary[id], id, TouchActionType.Released, screenPointerCoords, false);
135 | }
136 | }
137 | idToEffectDictionary.Remove(id);
138 | break;
139 |
140 | case MotionEventActions.Cancel:
141 | if (capture)
142 | {
143 | FireEvent(this, id, TouchActionType.Cancelled, screenPointerCoords, false);
144 | }
145 | else
146 | {
147 | if (idToEffectDictionary[id] != null)
148 | {
149 | FireEvent(idToEffectDictionary[id], id, TouchActionType.Cancelled, screenPointerCoords, false);
150 | }
151 | }
152 | idToEffectDictionary.Remove(id);
153 | break;
154 | }
155 | }
156 |
157 | void CheckForBoundaryHop(int id, Point pointerLocation)
158 | {
159 | TouchEffect touchEffectHit = null;
160 |
161 | foreach (Android.Views.View view in viewDictionary.Keys)
162 | {
163 | // Get the view rectangle
164 | try
165 | {
166 | view.GetLocationOnScreen(twoIntArray);
167 | }
168 | catch // System.ObjectDisposedException: Cannot access a disposed object.
169 | {
170 | continue;
171 | }
172 | Rectangle viewRect = new Rectangle(twoIntArray[0], twoIntArray[1], view.Width, view.Height);
173 |
174 | if (viewRect.Contains(pointerLocation))
175 | {
176 | touchEffectHit = viewDictionary[view];
177 | }
178 | }
179 |
180 | if (touchEffectHit != idToEffectDictionary[id])
181 | {
182 | if (idToEffectDictionary[id] != null)
183 | {
184 | FireEvent(idToEffectDictionary[id], id, TouchActionType.Exited, pointerLocation, true);
185 | }
186 | if (touchEffectHit != null)
187 | {
188 | FireEvent(touchEffectHit, id, TouchActionType.Entered, pointerLocation, true);
189 | }
190 | idToEffectDictionary[id] = touchEffectHit;
191 | }
192 | }
193 |
194 | void FireEvent(TouchEffect touchEffect, int id, TouchActionType actionType, Point pointerLocation, bool isInContact)
195 | {
196 | // Get the method to call for firing events
197 | Action onTouchAction = touchEffect.libTouchEffect.OnTouchAction;
198 |
199 | // Get the location of the pointer within the view
200 | touchEffect.view.GetLocationOnScreen(twoIntArray);
201 | double x = pointerLocation.X - twoIntArray[0];
202 | double y = pointerLocation.Y - twoIntArray[1];
203 | Point point = new Point(fromPixels(x), fromPixels(y));
204 |
205 | // Call the method
206 | onTouchAction(touchEffect.formsElement,
207 | new TouchActionEventArgs(id, actionType, point, isInContact));
208 | }
209 | }
210 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/MainActivity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Android.App;
4 | using Android.Content.PM;
5 | using Android.Runtime;
6 | using Android.Views;
7 | using Android.Widget;
8 | using Android.OS;
9 |
10 | namespace CouchDraw.Droid
11 | {
12 | [Activity(Label = "CouchDraw", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
13 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
14 | {
15 | protected override void OnCreate(Bundle savedInstanceState)
16 | {
17 | TabLayoutResource = Resource.Layout.Tabbar;
18 | ToolbarResource = Resource.Layout.Toolbar;
19 |
20 | base.OnCreate(savedInstanceState);
21 |
22 | Xamarin.Essentials.Platform.Init(this, savedInstanceState);
23 | global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
24 |
25 | Couchbase.Lite.Support.Droid.Activate(this);
26 |
27 | LoadApplication(new App());
28 | }
29 | public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
30 | {
31 | Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
32 |
33 | base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Properties/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using Android.App;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle("CouchDraw.Android")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("CouchDraw.Android")]
14 | [assembly: AssemblyCopyright("Copyright © 2014")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 | [assembly: ComVisible(false)]
18 |
19 | // Version information for an assembly consists of the following four values:
20 | //
21 | // Major Version
22 | // Minor Version
23 | // Build Number
24 | // Revision
25 | //
26 | // You can specify all the values or you can default the Build and Revision Numbers
27 | // by using the '*' as shown below:
28 | // [assembly: AssemblyVersion("1.0.*")]
29 | [assembly: AssemblyVersion("1.0.0.0")]
30 | [assembly: AssemblyFileVersion("1.0.0.0")]
31 |
32 | // Add some common permissions, these can be removed if not needed
33 | [assembly: UsesPermission(Android.Manifest.Permission.Internet)]
34 | [assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]
35 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/AboutResources.txt:
--------------------------------------------------------------------------------
1 | Images, layout descriptions, binary blobs and string dictionaries can be included
2 | in your application as resource files. Various Android APIs are designed to
3 | operate on the resource IDs instead of dealing with images, strings or binary blobs
4 | directly.
5 |
6 | For example, a sample Android app that contains a user interface layout (main.xml),
7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
8 | would keep its resources in the "Resources" directory of the application:
9 |
10 | Resources/
11 | drawable-hdpi/
12 | icon.png
13 |
14 | drawable-ldpi/
15 | icon.png
16 |
17 | drawable-mdpi/
18 | icon.png
19 |
20 | layout/
21 | main.xml
22 |
23 | values/
24 | strings.xml
25 |
26 | In order to get the build system to recognize Android resources, set the build action to
27 | "AndroidResource". The native Android APIs do not operate directly with filenames, but
28 | instead operate on resource IDs. When you compile an Android application that uses resources,
29 | the build system will package the resources for distribution and generate a class called
30 | "Resource" that contains the tokens for each one of the resources included. For example,
31 | for the above Resources layout, this is what the Resource class would expose:
32 |
33 | public class Resource {
34 | public class drawable {
35 | public const int icon = 0x123;
36 | }
37 |
38 | public class layout {
39 | public const int main = 0x456;
40 | }
41 |
42 | public class strings {
43 | public const int first_string = 0xabc;
44 | public const int second_string = 0xbcd;
45 | }
46 | }
47 |
48 | You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
49 | to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
50 | string in the dictionary file values/strings.xml.
51 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/layout/Tabbar.axml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/layout/Toolbar.axml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-anydpi-v26/icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-anydpi-v26/icon_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-hdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-hdpi/icon.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-hdpi/launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-hdpi/launcher_foreground.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-mdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-mdpi/icon.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-mdpi/launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-mdpi/launcher_foreground.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-xhdpi/icon.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-xhdpi/launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-xhdpi/launcher_foreground.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-xxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-xxhdpi/icon.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-xxhdpi/launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-xxhdpi/launcher_foreground.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-xxxhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-xxxhdpi/icon.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.Android/Resources/mipmap-xxxhdpi/launcher_foreground.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 | #3F51B5
5 | #303F9F
6 | #FF4081
7 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Android/Resources/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
24 |
27 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Core/AppInstance.cs:
--------------------------------------------------------------------------------
1 | namespace CouchDraw.Core
2 | {
3 | public static class AppInstance
4 | {
5 | // Unique ID so canvas additions can be differentiated
6 | public static string AppId { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Core/CouchDraw.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Core/Repositories/ICanvasRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using CouchDraw.Models;
5 |
6 | namespace CouchDraw.Core.Repositories
7 | {
8 | public interface ICanvasRepository : IDisposable
9 | {
10 | void SavePath(Path path);
11 | void DeletePath(Path path);
12 | Task> GetInternalPathsAsync();
13 | List GetInternalPaths();
14 | List GetExternalPaths(Action> pathsUpdated);
15 | void DeletePaths(List paths);
16 | void DeleteAllPaths();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Core/ViewModels/MainViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Collections.Generic;
4 | using System.Threading.Tasks;
5 | using CouchDraw.Core.Repositories;
6 | using CouchDraw.Models;
7 | using Robo.Mvvm;
8 | using Robo.Mvvm.ViewModels;
9 | using System.Windows.Input;
10 | using Robo.Mvvm.Input;
11 |
12 | namespace CouchDraw.Core.ViewModels
13 | {
14 | public class MainViewModel : BaseViewModel
15 | {
16 | Action UpdateCanvas { get; set; }
17 | string SelectedPathColor { get; set; } = "#000000";
18 |
19 | public List Paths { get; set; } = new List();
20 | public List ExternalPaths = new List();
21 |
22 |
23 | ICommand _selectPathColorCommand;
24 | public ICommand SelectPathColorCommand
25 | {
26 | get
27 | {
28 | if (_selectPathColorCommand == null)
29 | {
30 | _selectPathColorCommand = new Command((color) => SelectedPathColor = color);
31 | }
32 |
33 | return _selectPathColorCommand;
34 | }
35 | }
36 |
37 | ICommand _undoPathCommand;
38 | public ICommand UndoPathCommand
39 | {
40 | get
41 | {
42 | if (_undoPathCommand == null)
43 | {
44 | _undoPathCommand = new Command(UndoLastPath);
45 | }
46 |
47 | return _undoPathCommand;
48 | }
49 | }
50 |
51 | ICommand _clearAllPathsCommand;
52 | public ICommand ClearAllPathsCommand
53 | {
54 | get
55 | {
56 | if (_clearAllPathsCommand == null)
57 | {
58 | _clearAllPathsCommand = new Command(ClearAllPaths);
59 | }
60 |
61 | return _clearAllPathsCommand;
62 | }
63 | }
64 |
65 | ICommand _blankCanvas;
66 | public ICommand BlankCanvas
67 | {
68 | get
69 | {
70 | if (_blankCanvas == null)
71 | {
72 | _blankCanvas = new Command(ClearAll);
73 | }
74 |
75 | return _blankCanvas;
76 | }
77 | }
78 |
79 |
80 | ICommand _forceUpdate;
81 | public ICommand ForceUpdateCommand
82 | {
83 | get
84 | {
85 | if (_forceUpdate == null)
86 | {
87 | _forceUpdate = new Command(forceUpdate);
88 | }
89 |
90 | return _forceUpdate;
91 | }
92 | }
93 |
94 |
95 | ICanvasRepository CanvasRepository { get; set; }
96 |
97 | public MainViewModel(Action updateCanvas)
98 | {
99 | UpdateCanvas = updateCanvas;
100 |
101 | CanvasRepository = ServiceContainer.GetInstance();
102 |
103 | Init();
104 | }
105 |
106 | // We want to kick off this method, we don't need to (a)wait for the response.
107 | private async void Init()
108 | {
109 | var internalPaths = await CanvasRepository.GetInternalPathsAsync().ConfigureAwait(false);
110 | var externalPaths = await Task.Run(() => CanvasRepository.GetExternalPaths(UpdatePaths));
111 |
112 | if (internalPaths?.Count > 0)
113 | {
114 | Paths = internalPaths;
115 | } else
116 | {
117 | Paths = new List();
118 | }
119 |
120 | if (externalPaths?.Count > 0)
121 | {
122 | ExternalPaths = externalPaths;
123 | } else
124 | {
125 | ExternalPaths = new List();
126 | }
127 |
128 | UpdateCanvas?.Invoke();
129 |
130 | }
131 |
132 | public void CreatePath(Point point)
133 | {
134 | var path = new Path(Guid.NewGuid().ToString())
135 | {
136 | CreatedBy = AppInstance.AppId,
137 | Color = SelectedPathColor
138 | };
139 |
140 | path.Points.Add(point);
141 |
142 | Paths.Add(path);
143 |
144 | SavePath(path);
145 | }
146 |
147 | public void AddPoint(Point point)
148 | {
149 |
150 |
151 | if (Paths != null)
152 | {
153 | var path = Paths.Last();
154 |
155 | if (path != null)
156 | {
157 | path.Points?.Add(point);
158 | SavePath(path);
159 | }
160 | }
161 | }
162 |
163 | void SavePath(Path path)
164 | {
165 | Task.Run(() => CanvasRepository?.SavePath(path));
166 | UpdateCanvas?.Invoke();
167 | }
168 |
169 | void UpdatePaths(List paths)
170 | {
171 | ExternalPaths = paths;
172 | var internalPaths = CanvasRepository.GetInternalPaths();
173 |
174 |
175 |
176 | if (internalPaths == null || internalPaths?.Count == 0)
177 | {
178 | Paths = new List();
179 | }
180 |
181 |
182 | UpdateCanvas?.Invoke();
183 | }
184 |
185 | void UndoLastPath()
186 | {
187 | var path = Paths?.Last();
188 |
189 | if (path != null)
190 | {
191 | CanvasRepository?.DeletePath(path);
192 | Paths.Remove(path);
193 | UpdateCanvas?.Invoke();
194 | }
195 | }
196 |
197 | void ClearAllPaths()
198 | {
199 | CanvasRepository?.DeletePaths(Paths);
200 | Paths = new List();
201 | UpdateCanvas?.Invoke();
202 | }
203 |
204 |
205 | void ClearAll()
206 | {
207 |
208 |
209 | CanvasRepository?.DeleteAllPaths();
210 | Paths = new List();
211 | UpdateCanvas?.Invoke();
212 |
213 | Paths = new List();
214 | ExternalPaths = new List();
215 | }
216 |
217 | void forceUpdate()
218 | {
219 | Init();
220 | CheckInternetConnection();
221 | }
222 |
223 |
224 | public bool CheckInternetConnection()
225 | {
226 | string CheckUrl = "http://google.com";
227 |
228 | try
229 | {
230 | System.Net.HttpWebRequest iNetRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(CheckUrl);
231 |
232 | iNetRequest.Timeout = 3000;
233 |
234 | System.Net.WebResponse iNetResponse = iNetRequest.GetResponse();
235 |
236 | Acr.UserDialogs.UserDialogs.Instance.Toast("Internet is up! ");
237 | iNetResponse.Close();
238 |
239 | return true;
240 |
241 | }
242 | catch (System.Net.WebException ex)
243 | {
244 | Acr.UserDialogs.UserDialogs.Instance.Toast("Oh... internet is down! but your changes were saved locally.");
245 |
246 | return false;
247 | }
248 | }
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Models/CouchDraw.Models.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Models/Path.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CouchDraw.Models
4 | {
5 | public class Path
6 | {
7 | public string Type => "path";
8 | public string Id { get; set; }
9 | public string Color { get; set; } = "#000000";
10 | public string CreatedBy { get; set; }
11 | public List Points { get; set; } = new List();
12 |
13 | public Path(string id)
14 | {
15 | Id = id;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Models/Point.cs:
--------------------------------------------------------------------------------
1 | namespace CouchDraw.Models
2 | {
3 | public class Point
4 | {
5 | public string Type => "point";
6 | public double X { get; set; }
7 | public double Y { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Repositories/CanvasRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Couchbase.Lite;
6 | using Couchbase.Lite.Query;
7 | using CouchDraw.Core;
8 | using CouchDraw.Core.Repositories;
9 | using CouchDraw.Models;
10 |
11 | namespace CouchDraw.Repositories
12 | {
13 | public class CanvasRepository : ICanvasRepository
14 | {
15 | const string databaseName = "couchdraw";
16 |
17 | IQuery _pathsQuery;
18 | ListenerToken _pathsQueryToken;
19 |
20 | protected DatabaseManager _databaseManager;
21 | protected DatabaseManager DatabaseManager
22 | {
23 | get
24 | {
25 | if (_databaseManager == null)
26 | {
27 | _databaseManager = new DatabaseManager(databaseName);
28 | }
29 |
30 | return _databaseManager;
31 | }
32 | }
33 |
34 | public CanvasRepository()
35 | {
36 | DatabaseManager.StartReplication();
37 | }
38 |
39 | public void SavePath(Path path) => DatabaseManager.Database.Save(path.ToMutableDocument($"path::{path.Id}"));
40 |
41 | public async Task> GetInternalPathsAsync()
42 | {
43 | var paths = new List();
44 |
45 | try
46 | {
47 | var pathsQuery = QueryBuilder
48 | .Select(SelectResult.All())
49 | .From(DataSource.Database(DatabaseManager.Database))
50 | .Where((Expression.Property("type").EqualTo(Expression.String("path"))
51 | .And(Expression.Property("createdBy").EqualTo(Expression.String(AppInstance.AppId)))));
52 |
53 | paths = await Task.Run(() =>
54 | {
55 | return pathsQuery.Execute()?.AllResults()?.ToObjects()?.ToList();
56 | });
57 | }
58 | catch (Exception ex)
59 | {
60 | Console.WriteLine($"CanvasRepository GetInternalPaths Exception: {ex.Message}");
61 | }
62 |
63 | return paths;
64 | }
65 |
66 |
67 | public List GetInternalPaths()
68 | {
69 | var paths = new List();
70 |
71 | try
72 | {
73 | var pathsQuery = QueryBuilder
74 | .Select(SelectResult.All())
75 | .From(DataSource.Database(DatabaseManager.Database))
76 | .Where((Expression.Property("type").EqualTo(Expression.String("path"))
77 | .And(Expression.Property("createdBy").EqualTo(Expression.String(AppInstance.AppId)))));
78 |
79 |
80 | return pathsQuery.Execute()?.AllResults()?.ToObjects()?.ToList();
81 | }
82 | catch (Exception ex)
83 | {
84 | Console.WriteLine($"CanvasRepository GetInternalPaths Exception: {ex.Message}");
85 | }
86 |
87 | return paths;
88 | }
89 |
90 | public List GetExternalPaths(Action> pathsUpdated)
91 | {
92 | List paths = new List();
93 |
94 | try
95 | {
96 | _pathsQuery = QueryBuilder
97 | .Select(SelectResult.All())
98 | .From(DataSource.Database(DatabaseManager.Database))
99 | .Where((Expression.Property("type").EqualTo(Expression.String("path"))
100 | .And(Expression.Property("createdBy").NotEqualTo(Expression.String(AppInstance.AppId)))));
101 |
102 | if (pathsUpdated != null)
103 | {
104 | _pathsQueryToken = _pathsQuery.AddChangeListener((object sender, QueryChangedEventArgs e) =>
105 | {
106 |
107 | //Acr.UserDialogs.UserDialogs.Instance.Toast("Something changed", new TimeSpan(3000));
108 |
109 | if (e?.Results != null && e.Error == null)
110 | {
111 | paths = e.Results.AllResults()?.ToObjects() as List;
112 |
113 | if (paths != null)
114 | {
115 | pathsUpdated.Invoke(paths);
116 | } else
117 | {
118 | pathsUpdated.Invoke(new List());
119 | }
120 | }
121 | });
122 | }
123 | }
124 | catch (Exception ex)
125 | {
126 | Console.WriteLine($"CanvasRepository GetExternalPaths Exception: {ex.Message}");
127 | }
128 |
129 | return paths;
130 | }
131 |
132 | public void DeletePaths(List paths)
133 | {
134 | foreach (var path in paths)
135 | {
136 | DeletePath(path);
137 | }
138 | }
139 |
140 | public void DeletePath(Path path)
141 | {
142 | var document = DatabaseManager.Database.GetDocument($"path::{path.Id}");
143 |
144 | if (document != null)
145 | {
146 | DatabaseManager.Database.Delete(document);
147 | }
148 | }
149 |
150 | public void DeleteAllPaths()
151 | {
152 | try
153 | {
154 | var pathsQuery = QueryBuilder
155 | .Select(SelectResult.All())
156 | .From(DataSource.Database(DatabaseManager.Database));
157 |
158 |
159 | List paths = pathsQuery.Execute()?.AllResults()?.ToObjects()?.ToList();
160 | DeletePaths(paths);
161 |
162 |
163 | }
164 | catch (Exception ex)
165 | {
166 | Console.WriteLine($"CanvasRepository GetInternalPaths Exception: {ex.Message}");
167 | }
168 | }
169 |
170 | public void Dispose()
171 | {
172 | _pathsQuery.RemoveChangeListener(_pathsQueryToken);
173 | _pathsQuery = null;
174 |
175 | DatabaseManager?.Dispose();
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Repositories/CouchDraw.Repositories.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.Repositories/DatabaseManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Couchbase.Lite;
3 | using Couchbase.Lite.Sync;
4 |
5 | namespace CouchDraw.Repositories
6 | {
7 | public class DatabaseManager : IDisposable
8 | {
9 | // Note: User 'localhost' or '127.0.0.1' when using a simulator
10 | //readonly Uri _remoteSyncUrl = new Uri("ws://52.53.240.207:4984");
11 | readonly Uri _remoteSyncUrl = new Uri("ws://127.0.0.1:4984");
12 |
13 | // Note: Use '10.0.2.2' when using an emulator
14 | //readonly Uri _remoteSyncUrl = new Uri("ws://10.0.2.2:4984");
15 |
16 | readonly string _databaseName;
17 |
18 | Replicator _replicator;
19 | ListenerToken _replicatorListenerToken;
20 |
21 | Database _database;
22 | public Database Database
23 | {
24 | get
25 | {
26 | if (_database == null)
27 | {
28 | _database = new Database(_databaseName);
29 | }
30 |
31 | return _database;
32 | }
33 | }
34 |
35 | public DatabaseManager(string databaseName)
36 | {
37 | _databaseName = databaseName;
38 | }
39 |
40 | public void StartReplication()
41 | {
42 | try
43 | {
44 | var targetUrlEndpoint = new URLEndpoint(new Uri(_remoteSyncUrl, _databaseName));
45 |
46 | var configuration = new ReplicatorConfiguration(Database, targetUrlEndpoint)
47 | {
48 | ReplicatorType = ReplicatorType.PushAndPull,
49 | Continuous = true
50 | };
51 |
52 | _replicator = new Replicator(configuration);
53 |
54 | _replicatorListenerToken = _replicator.AddChangeListener(OnReplicatorUpdate);
55 |
56 | _replicator.Start();
57 | }
58 | catch(Exception ex)
59 | {
60 | // We don't want replication errors to prevent us from
61 | // using the app, but we do want to know about them.
62 | Console.WriteLine($"Replication Exception - {ex.Message}");
63 | }
64 | }
65 |
66 | void OnReplicatorUpdate(object sender, ReplicatorStatusChangedEventArgs e)
67 | {
68 | var status = e.Status;
69 |
70 | switch (status.Activity)
71 | {
72 | case ReplicatorActivityLevel.Busy:
73 | Console.WriteLine("Busy transferring data.");
74 | break;
75 | case ReplicatorActivityLevel.Connecting:
76 | Console.WriteLine("Connecting to Sync Gateway.");
77 | break;
78 | case ReplicatorActivityLevel.Idle:
79 | Console.WriteLine("Replicator in idle state.");
80 | break;
81 | case ReplicatorActivityLevel.Offline:
82 | Console.WriteLine("Replicator in offline state.");
83 | break;
84 | case ReplicatorActivityLevel.Stopped:
85 | Console.WriteLine("Completed syncing documents.");
86 | break;
87 | }
88 |
89 | if (status.Progress.Completed == status.Progress.Total)
90 | {
91 | Console.WriteLine("All documents synced.");
92 | }
93 | else
94 | {
95 | Console.WriteLine($"Documents {status.Progress.Total - status.Progress.Completed} still pending sync");
96 | }
97 | }
98 |
99 | public void StopReplication()
100 | {
101 | if (_replicator != null)
102 | {
103 | _replicator.RemoveChangeListener(_replicatorListenerToken);
104 | _replicator.Stop();
105 | }
106 | }
107 |
108 | public void Dispose()
109 | {
110 | try
111 | {
112 | if (_replicator != null)
113 | {
114 | StopReplication();
115 |
116 | // Because the 'Stop' method for a Replicator instance is asynchronous
117 | // we must wait for the status activity to be stopped before closing the database.
118 | while (true)
119 | {
120 | if (_replicator.Status.Activity == ReplicatorActivityLevel.Stopped)
121 | {
122 | break;
123 | }
124 | }
125 |
126 | _replicator.Dispose();
127 | }
128 |
129 | _database.Close();
130 | _database = null;
131 | }
132 | catch (Exception ex)
133 | {
134 | Console.WriteLine(ex.Message);
135 | }
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | using Foundation;
6 | using UIKit;
7 |
8 | namespace CouchDraw.iOS
9 | {
10 | // The UIApplicationDelegate for the application. This class is responsible for launching the
11 | // User Interface of the application, as well as listening (and optionally responding) to
12 | // application events from iOS.
13 | [Register("AppDelegate")]
14 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
15 | {
16 | //
17 | // This method is invoked when the application has loaded and is ready to run. In this
18 | // method you should instantiate the window, load the UI into it and then make the window
19 | // visible.
20 | //
21 | // You have 17 seconds to return from this method, or iOS will terminate your application.
22 | //
23 | public override bool FinishedLaunching(UIApplication app, NSDictionary options)
24 | {
25 | global::Xamarin.Forms.Forms.Init();
26 |
27 | Couchbase.Lite.Support.iOS.Activate();
28 |
29 | LoadApplication(new App());
30 |
31 | return base.FinishedLaunching(app, options);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "scale": "2x",
5 | "size": "20x20",
6 | "idiom": "iphone",
7 | "filename": "Icon40.png"
8 | },
9 | {
10 | "scale": "3x",
11 | "size": "20x20",
12 | "idiom": "iphone",
13 | "filename": "Icon60.png"
14 | },
15 | {
16 | "scale": "2x",
17 | "size": "29x29",
18 | "idiom": "iphone",
19 | "filename": "Icon58.png"
20 | },
21 | {
22 | "scale": "3x",
23 | "size": "29x29",
24 | "idiom": "iphone",
25 | "filename": "Icon87.png"
26 | },
27 | {
28 | "scale": "2x",
29 | "size": "40x40",
30 | "idiom": "iphone",
31 | "filename": "Icon80.png"
32 | },
33 | {
34 | "scale": "3x",
35 | "size": "40x40",
36 | "idiom": "iphone",
37 | "filename": "Icon120.png"
38 | },
39 | {
40 | "scale": "2x",
41 | "size": "60x60",
42 | "idiom": "iphone",
43 | "filename": "Icon120.png"
44 | },
45 | {
46 | "scale": "3x",
47 | "size": "60x60",
48 | "idiom": "iphone",
49 | "filename": "Icon180.png"
50 | },
51 | {
52 | "scale": "1x",
53 | "size": "20x20",
54 | "idiom": "ipad",
55 | "filename": "Icon20.png"
56 | },
57 | {
58 | "scale": "2x",
59 | "size": "20x20",
60 | "idiom": "ipad",
61 | "filename": "Icon40.png"
62 | },
63 | {
64 | "scale": "1x",
65 | "size": "29x29",
66 | "idiom": "ipad",
67 | "filename": "Icon29.png"
68 | },
69 | {
70 | "scale": "2x",
71 | "size": "29x29",
72 | "idiom": "ipad",
73 | "filename": "Icon58.png"
74 | },
75 | {
76 | "scale": "1x",
77 | "size": "40x40",
78 | "idiom": "ipad",
79 | "filename": "Icon40.png"
80 | },
81 | {
82 | "scale": "2x",
83 | "size": "40x40",
84 | "idiom": "ipad",
85 | "filename": "Icon80.png"
86 | },
87 | {
88 | "scale": "1x",
89 | "size": "76x76",
90 | "idiom": "ipad",
91 | "filename": "Icon76.png"
92 | },
93 | {
94 | "scale": "2x",
95 | "size": "76x76",
96 | "idiom": "ipad",
97 | "filename": "Icon152.png"
98 | },
99 | {
100 | "scale": "2x",
101 | "size": "83.5x83.5",
102 | "idiom": "ipad",
103 | "filename": "Icon167.png"
104 | },
105 | {
106 | "scale": "1x",
107 | "size": "1024x1024",
108 | "idiom": "ios-marketing",
109 | "filename": "Icon1024.png"
110 | }
111 | ],
112 | "properties": {},
113 | "info": {
114 | "version": 1,
115 | "author": "xcode"
116 | }
117 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon1024.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon120.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon152.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon167.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon180.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon20.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon29.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon40.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon58.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon60.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon76.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon80.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Assets.xcassets/AppIcon.appiconset/Icon87.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/CouchDraw.iOS.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | iPhoneSimulator
6 | 8.0.30703
7 | 2.0
8 | {39A95543-7220-4A73-8525-FB0D4836B78D}
9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
10 | {6143fdea-f3c2-4a09-aafa-6e230626515e}
11 | Exe
12 | CouchDraw.iOS
13 | Resources
14 | CouchDraw.iOS
15 | true
16 | NSUrlSessionHandler
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\iPhoneSimulator\Debug
23 | DEBUG
24 | prompt
25 | 4
26 | false
27 | x86_64
28 | None
29 | true
30 |
31 |
32 | none
33 | true
34 | bin\iPhoneSimulator\Release
35 | prompt
36 | 4
37 | None
38 | x86_64
39 | false
40 |
41 |
42 | true
43 | full
44 | false
45 | bin\iPhone\Debug
46 | DEBUG
47 | prompt
48 | 4
49 | false
50 | ARM64
51 | iPhone Developer
52 | true
53 | Entitlements.plist
54 |
55 |
56 | none
57 | true
58 | bin\iPhone\Release
59 | prompt
60 | 4
61 | ARM64
62 | false
63 | iPhone Developer
64 | Entitlements.plist
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | false
79 |
80 |
81 | false
82 |
83 |
84 | false
85 |
86 |
87 | false
88 |
89 |
90 | false
91 |
92 |
93 | false
94 |
95 |
96 | false
97 |
98 |
99 | false
100 |
101 |
102 | false
103 |
104 |
105 | false
106 |
107 |
108 | false
109 |
110 |
111 | false
112 |
113 |
114 | false
115 |
116 |
117 | false
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | 1.68.0
133 |
134 |
135 | 1.68.0
136 |
137 |
138 | 2.6.0
139 |
140 |
141 |
142 |
143 |
144 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}
145 | CouchDraw
146 |
147 |
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Effects/TouchEffect.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | using Xamarin.Forms;
5 | using Xamarin.Forms.Platform.iOS;
6 |
7 | using UIKit;
8 |
9 | [assembly: ResolutionGroupName("CouchDraw")]
10 | [assembly: ExportEffect(typeof(TouchTracking.iOS.TouchEffect), "TouchEffect")]
11 |
12 | namespace TouchTracking.iOS
13 | {
14 | public class TouchEffect : PlatformEffect
15 | {
16 | UIView view;
17 | TouchRecognizer touchRecognizer;
18 |
19 | protected override void OnAttached()
20 | {
21 | // Get the iOS UIView corresponding to the Element that the effect is attached to
22 | view = Control == null ? Container : Control;
23 |
24 | // Get access to the TouchEffect class in the .NET Standard library
25 | TouchTracking.TouchEffect effect = (TouchTracking.TouchEffect)Element.Effects.FirstOrDefault(e => e is TouchTracking.TouchEffect);
26 |
27 | if (effect != null && view != null)
28 | {
29 | // Create a TouchRecognizer for this UIView
30 | touchRecognizer = new TouchRecognizer(Element, view, effect);
31 | view.AddGestureRecognizer(touchRecognizer);
32 | }
33 | }
34 |
35 | protected override void OnDetached()
36 | {
37 | if (touchRecognizer != null)
38 | {
39 | // Clean up the TouchRecognizer object
40 | touchRecognizer.Detach();
41 |
42 | // Remove the TouchRecognizer from the UIView
43 | view.RemoveGestureRecognizer(touchRecognizer);
44 | }
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Effects/TouchRecognizer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | using Xamarin.Forms;
6 |
7 | using CoreGraphics;
8 | using Foundation;
9 | using UIKit;
10 |
11 | namespace TouchTracking.iOS
12 | {
13 | class TouchRecognizer : UIGestureRecognizer
14 | {
15 | Element element; // Forms element for firing events
16 | UIView view; // iOS UIView
17 | TouchTracking.TouchEffect touchEffect;
18 | bool capture;
19 |
20 | static Dictionary viewDictionary =
21 | new Dictionary();
22 |
23 | static Dictionary idToTouchDictionary =
24 | new Dictionary();
25 |
26 | public TouchRecognizer(Element element, UIView view, TouchTracking.TouchEffect touchEffect)
27 | {
28 | this.element = element;
29 | this.view = view;
30 | this.touchEffect = touchEffect;
31 |
32 | viewDictionary.Add(view, this);
33 | }
34 |
35 | public void Detach()
36 | {
37 | viewDictionary.Remove(view);
38 | }
39 |
40 | // touches = touches of interest; evt = all touches of type UITouch
41 | public override void TouchesBegan(NSSet touches, UIEvent evt)
42 | {
43 | base.TouchesBegan(touches, evt);
44 |
45 | foreach (UITouch touch in touches.Cast())
46 | {
47 | long id = touch.Handle.ToInt64();
48 | FireEvent(this, id, TouchActionType.Pressed, touch, true);
49 |
50 | if (!idToTouchDictionary.ContainsKey(id))
51 | {
52 | idToTouchDictionary.Add(id, this);
53 | }
54 | }
55 |
56 | // Save the setting of the Capture property
57 | capture = touchEffect.Capture;
58 | }
59 |
60 | public override void TouchesMoved(NSSet touches, UIEvent evt)
61 | {
62 | base.TouchesMoved(touches, evt);
63 |
64 | foreach (UITouch touch in touches.Cast())
65 | {
66 | long id = touch.Handle.ToInt64();
67 |
68 | if (capture)
69 | {
70 | FireEvent(this, id, TouchActionType.Moved, touch, true);
71 | }
72 | else
73 | {
74 | CheckForBoundaryHop(touch);
75 |
76 | if (idToTouchDictionary[id] != null)
77 | {
78 | FireEvent(idToTouchDictionary[id], id, TouchActionType.Moved, touch, true);
79 | }
80 | }
81 | }
82 | }
83 |
84 | public override void TouchesEnded(NSSet touches, UIEvent evt)
85 | {
86 | base.TouchesEnded(touches, evt);
87 |
88 | foreach (UITouch touch in touches.Cast())
89 | {
90 | long id = touch.Handle.ToInt64();
91 |
92 | if (capture)
93 | {
94 | FireEvent(this, id, TouchActionType.Released, touch, false);
95 | }
96 | else
97 | {
98 | CheckForBoundaryHop(touch);
99 |
100 | if (idToTouchDictionary[id] != null)
101 | {
102 | FireEvent(idToTouchDictionary[id], id, TouchActionType.Released, touch, false);
103 | }
104 | }
105 | idToTouchDictionary.Remove(id);
106 | }
107 | }
108 |
109 | public override void TouchesCancelled(NSSet touches, UIEvent evt)
110 | {
111 | base.TouchesCancelled(touches, evt);
112 |
113 | foreach (UITouch touch in touches.Cast())
114 | {
115 | long id = touch.Handle.ToInt64();
116 |
117 | if (capture)
118 | {
119 | FireEvent(this, id, TouchActionType.Cancelled, touch, false);
120 | }
121 | else if (idToTouchDictionary[id] != null)
122 | {
123 | FireEvent(idToTouchDictionary[id], id, TouchActionType.Cancelled, touch, false);
124 | }
125 | idToTouchDictionary.Remove(id);
126 | }
127 | }
128 |
129 | void CheckForBoundaryHop(UITouch touch)
130 | {
131 | long id = touch.Handle.ToInt64();
132 |
133 | // TODO: Might require converting to a List for multiple hits
134 | TouchRecognizer recognizerHit = null;
135 |
136 | foreach (UIView view in viewDictionary.Keys)
137 | {
138 | CGPoint location = touch.LocationInView(view);
139 |
140 | if (new CGRect(new CGPoint(), view.Frame.Size).Contains(location))
141 | {
142 | recognizerHit = viewDictionary[view];
143 | }
144 | }
145 | if (recognizerHit != idToTouchDictionary[id])
146 | {
147 | if (idToTouchDictionary[id] != null)
148 | {
149 | FireEvent(idToTouchDictionary[id], id, TouchActionType.Exited, touch, true);
150 | }
151 | if (recognizerHit != null)
152 | {
153 | FireEvent(recognizerHit, id, TouchActionType.Entered, touch, true);
154 | }
155 | idToTouchDictionary[id] = recognizerHit;
156 | }
157 | }
158 |
159 | void FireEvent(TouchRecognizer recognizer, long id, TouchActionType actionType, UITouch touch, bool isInContact)
160 | {
161 | // Convert touch location to Xamarin.Forms Point value
162 | CGPoint cgPoint = touch.LocationInView(recognizer.View);
163 | Point xfPoint = new Point(cgPoint.X, cgPoint.Y);
164 |
165 | // Get the method to call for firing events
166 | Action onTouchAction = recognizer.touchEffect.OnTouchAction;
167 |
168 | // Call that method
169 | onTouchAction(recognizer.element,
170 | new TouchActionEventArgs(id, actionType, xfPoint, isInContact));
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Entitlements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIDeviceFamily
6 |
7 | 2
8 |
9 | UISupportedInterfaceOrientations
10 |
11 | UIInterfaceOrientationPortrait
12 | UIInterfaceOrientationLandscapeLeft
13 | UIInterfaceOrientationLandscapeRight
14 |
15 | UISupportedInterfaceOrientations~ipad
16 |
17 | UIInterfaceOrientationPortrait
18 | UIInterfaceOrientationPortraitUpsideDown
19 | UIInterfaceOrientationLandscapeLeft
20 | UIInterfaceOrientationLandscapeRight
21 |
22 | MinimumOSVersion
23 | 8.0
24 | CFBundleDisplayName
25 | CouchDraw
26 | CFBundleIdentifier
27 | com.couchbaselabs.CouchDraw
28 | CFBundleVersion
29 | 1.0
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | CFBundleName
33 | CouchDraw
34 | XSAppIconAssets
35 | Assets.xcassets/AppIcon.appiconset
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | using Foundation;
6 | using UIKit;
7 |
8 | namespace CouchDraw.iOS
9 | {
10 | public class Application
11 | {
12 | // This is the main entry point of the application.
13 | static void Main(string[] args)
14 | {
15 | // if you want to use a different Application Delegate class from "AppDelegate"
16 | // you can specify it here.
17 | UIApplication.Main(args, null, "AppDelegate");
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CouchDraw.iOS")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CouchDraw.iOS")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("72bdc44f-c588-44f3-b6df-9aace7daafdd")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Resources/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Resources/Default-568h@2x.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Resources/Default-Portrait.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Resources/Default-Portrait.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Resources/Default-Portrait@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Resources/Default-Portrait@2x.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Resources/Default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Resources/Default.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Resources/Default@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/couchbaselabs/CouchDraw/044f1c5a85cc6bf0021a272c50f5463125712152/src/xamarin/CouchDraw.iOS/Resources/Default@2x.png
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.iOS/Resources/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchDraw.Android", "CouchDraw.Android\CouchDraw.Android.csproj", "{380A192B-9661-4A64-BD8A-22134FF53A09}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchDraw.iOS", "CouchDraw.iOS\CouchDraw.iOS.csproj", "{39A95543-7220-4A73-8525-FB0D4836B78D}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchDraw", "CouchDraw\CouchDraw.csproj", "{A0B39816-DAB7-4624-B33C-2BFE3E05DF25}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Platforms", "Platforms", "{52A70EB2-BEB5-4D0E-A1E4-593CA08DD187}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchDraw.Core", "CouchDraw.Core\CouchDraw.Core.csproj", "{D877FE82-6119-4776-8FBF-22478BC1613C}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchDraw.Models", "CouchDraw.Models\CouchDraw.Models.csproj", "{6FE13280-BD33-45DA-9E6C-42BC59D9461B}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchDraw.Repositories", "CouchDraw.Repositories\CouchDraw.Repositories.csproj", "{827A9FD4-A61F-4992-987C-BC22F169DA4E}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | Debug|iPhoneSimulator = Debug|iPhoneSimulator
23 | Release|iPhoneSimulator = Release|iPhoneSimulator
24 | Debug|iPhone = Debug|iPhone
25 | Release|iPhone = Release|iPhone
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
33 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
34 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
35 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
36 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Debug|iPhone.ActiveCfg = Debug|Any CPU
37 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Debug|iPhone.Build.0 = Debug|Any CPU
38 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Release|iPhone.ActiveCfg = Release|Any CPU
39 | {380A192B-9661-4A64-BD8A-22134FF53A09}.Release|iPhone.Build.0 = Release|Any CPU
40 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
41 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
42 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator
43 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Release|Any CPU.Build.0 = Release|iPhoneSimulator
44 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
45 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
46 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
47 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
48 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Debug|iPhone.ActiveCfg = Debug|iPhone
49 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Debug|iPhone.Build.0 = Debug|iPhone
50 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Release|iPhone.ActiveCfg = Release|iPhone
51 | {39A95543-7220-4A73-8525-FB0D4836B78D}.Release|iPhone.Build.0 = Release|iPhone
52 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Release|Any CPU.Build.0 = Release|Any CPU
56 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
57 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
58 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
59 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
60 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Debug|iPhone.ActiveCfg = Debug|Any CPU
61 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Debug|iPhone.Build.0 = Debug|Any CPU
62 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Release|iPhone.ActiveCfg = Release|Any CPU
63 | {A0B39816-DAB7-4624-B33C-2BFE3E05DF25}.Release|iPhone.Build.0 = Release|Any CPU
64 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
65 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Debug|Any CPU.Build.0 = Debug|Any CPU
66 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Release|Any CPU.ActiveCfg = Release|Any CPU
67 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Release|Any CPU.Build.0 = Release|Any CPU
68 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
69 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
70 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
71 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
72 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
73 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Debug|iPhone.Build.0 = Debug|Any CPU
74 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Release|iPhone.ActiveCfg = Release|Any CPU
75 | {D877FE82-6119-4776-8FBF-22478BC1613C}.Release|iPhone.Build.0 = Release|Any CPU
76 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
77 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Debug|Any CPU.Build.0 = Debug|Any CPU
78 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Release|Any CPU.ActiveCfg = Release|Any CPU
79 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Release|Any CPU.Build.0 = Release|Any CPU
80 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
81 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
82 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
83 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
84 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
85 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Debug|iPhone.Build.0 = Debug|Any CPU
86 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Release|iPhone.ActiveCfg = Release|Any CPU
87 | {6FE13280-BD33-45DA-9E6C-42BC59D9461B}.Release|iPhone.Build.0 = Release|Any CPU
88 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
89 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
90 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
91 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Release|Any CPU.Build.0 = Release|Any CPU
92 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
93 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
94 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
95 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
96 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
97 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Debug|iPhone.Build.0 = Debug|Any CPU
98 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Release|iPhone.ActiveCfg = Release|Any CPU
99 | {827A9FD4-A61F-4992-987C-BC22F169DA4E}.Release|iPhone.Build.0 = Release|Any CPU
100 | EndGlobalSection
101 | GlobalSection(NestedProjects) = preSolution
102 | {380A192B-9661-4A64-BD8A-22134FF53A09} = {52A70EB2-BEB5-4D0E-A1E4-593CA08DD187}
103 | {39A95543-7220-4A73-8525-FB0D4836B78D} = {52A70EB2-BEB5-4D0E-A1E4-593CA08DD187}
104 | EndGlobalSection
105 | EndGlobal
106 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CouchDraw.Core.Repositories;
3 | using CouchDraw.Repositories;
4 | using Robo.Mvvm;
5 | using Xamarin.Essentials;
6 | using Xamarin.Forms;
7 |
8 | namespace CouchDraw
9 | {
10 | public partial class App : Application
11 | {
12 | public App()
13 | {
14 | InitializeComponent();
15 |
16 | Core.AppInstance.AppId = GetUniquePersistentId("app_id");
17 |
18 | ServiceContainer.Register(new CanvasRepository());
19 |
20 | MainPage = new MainPage();
21 |
22 | Acr.UserDialogs.ToastConfig.DefaultActionTextColor = Color.White;
23 | Acr.UserDialogs.ToastConfig.DefaultBackgroundColor = Color.CadetBlue;
24 | Acr.UserDialogs.ToastConfig.DefaultDuration = TimeSpan.FromSeconds(2.0);
25 | Acr.UserDialogs.ToastConfig.DefaultPosition = Acr.UserDialogs.ToastPosition.Top;
26 | }
27 |
28 | protected override void OnResume()
29 | {
30 | new DatabaseManager("couchdraw").StartReplication();
31 | }
32 |
33 | string GetUniquePersistentId(string key)
34 | {
35 | var id = Preferences.Get(key, string.Empty);
36 |
37 | if (string.IsNullOrWhiteSpace(id))
38 | {
39 | id = Guid.NewGuid().ToString();
40 | Preferences.Set(key, id);
41 | }
42 |
43 | return id;
44 | }
45 | }
46 |
47 |
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using Xamarin.Forms.Xaml;
2 |
3 | [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/CouchDraw.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | netstandard2.0
7 | true
8 | true
9 |
10 |
11 |
12 | pdbonly
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
33 |
34 |
40 |
41 |
47 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
61 |
62 |
64 |
65 |
67 |
68 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Xamarin.Forms;
3 | using TouchTracking;
4 | using SkiaSharp;
5 | using SkiaSharp.Views.Forms;
6 | using CouchDraw.Core.ViewModels;
7 | using System.ComponentModel;
8 | using System;
9 |
10 | namespace CouchDraw
11 | {
12 | [DesignTimeVisible(false)]
13 | public partial class MainPage : ContentPage
14 | {
15 | SKPaint paint = new SKPaint
16 | {
17 | Style = SKPaintStyle.Stroke,
18 | StrokeWidth = 10,
19 | StrokeCap = SKStrokeCap.Round,
20 | StrokeJoin = SKStrokeJoin.Round
21 | };
22 |
23 | MainViewModel ViewModel { get; set; }
24 |
25 | public MainPage()
26 | {
27 | InitializeComponent();
28 | BindingContext = ViewModel = new MainViewModel(UpdateCanvas);
29 | }
30 |
31 | void OnTouchEffectAction(object sender, TouchActionEventArgs args)
32 | {
33 | var point = new Models.Point
34 | {
35 | X = args.Location.X,
36 | Y = args.Location.Y
37 | };
38 |
39 | switch (args.Type)
40 | {
41 | case TouchActionType.Pressed:
42 | ViewModel.CreatePath(point);
43 | canvasView.InvalidateSurface();
44 | break;
45 | case TouchActionType.Moved:
46 | ViewModel.AddPoint(point);
47 | canvasView.InvalidateSurface();
48 | break;
49 | }
50 | }
51 |
52 | List GetSKPaths(List paths)
53 | {
54 | var skPaths = new List();
55 |
56 | foreach (var path in paths)
57 | {
58 | var point = path.Points[0];
59 |
60 | if (point != null)
61 | {
62 | var skPath = new ColorSKPath(path.Color);
63 |
64 | // The path needs to be started from the first touch point
65 | skPath.MoveTo(GetToSKPoint(new Point(point.X, point.Y)));
66 |
67 | for (int i = 1; i < path.Points.Count; i++)
68 | {
69 | point = path.Points[i];
70 |
71 | // Connect the last point in the path to the new point
72 | skPath.LineTo(GetToSKPoint(new Point(point.X, point.Y)));
73 | }
74 |
75 | skPaths.Add(skPath);
76 | }
77 | }
78 |
79 | return skPaths;
80 | }
81 |
82 | SKPoint GetToSKPoint(Point pt)
83 | {
84 | return new SKPoint((float)(canvasView.CanvasSize.Width * pt.X / canvasView.Width),
85 | (float)(canvasView.CanvasSize.Height * pt.Y / canvasView.Height));
86 | }
87 |
88 | void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
89 | {
90 | SKCanvas canvas = args.Surface.Canvas;
91 | canvas.Clear();
92 |
93 | foreach (ColorSKPath path in GetSKPaths(ViewModel.Paths))
94 | {
95 | paint.Color = path.Color;
96 | canvas.DrawPath(path, paint);
97 | }
98 |
99 | foreach (ColorSKPath path in GetSKPaths(ViewModel.ExternalPaths))
100 | {
101 | paint.Color = path.Color;
102 | canvas.DrawPath(path, paint);
103 | }
104 | }
105 |
106 | void UpdateCanvas() => Device.BeginInvokeOnMainThread(canvasView.InvalidateSurface);
107 | }
108 |
109 | public class ColorSKPath : SKPath
110 | {
111 | public SKColor Color { get; set; }
112 |
113 | public ColorSKPath(string hexColor)
114 | {
115 | Color = SKColor.Parse(hexColor);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/TouchTracking/TouchActionEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xamarin.Forms;
3 |
4 | namespace TouchTracking
5 | {
6 | public class TouchActionEventArgs : EventArgs
7 | {
8 | public TouchActionEventArgs(long id, TouchActionType type, Point location, bool isInContact)
9 | {
10 | Id = id;
11 | Type = type;
12 | Location = location;
13 | IsInContact = isInContact;
14 | }
15 |
16 | public long Id { private set; get; }
17 |
18 | public TouchActionType Type { private set; get; }
19 |
20 | public Point Location { private set; get; }
21 |
22 | public bool IsInContact { private set; get; }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/TouchTracking/TouchActionEventHandler.cs:
--------------------------------------------------------------------------------
1 | namespace TouchTracking
2 | {
3 | public delegate void TouchActionEventHandler(object sender, TouchActionEventArgs args);
4 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/TouchTracking/TouchActionType.cs:
--------------------------------------------------------------------------------
1 | namespace TouchTracking
2 | {
3 | public enum TouchActionType
4 | {
5 | Entered,
6 | Pressed,
7 | Moved,
8 | Released,
9 | Exited,
10 | Cancelled
11 | }
12 | }
--------------------------------------------------------------------------------
/src/xamarin/CouchDraw/TouchTracking/TouchEffect.cs:
--------------------------------------------------------------------------------
1 | using Xamarin.Forms;
2 |
3 | namespace TouchTracking
4 | {
5 | public class TouchEffect : RoutingEffect
6 | {
7 | public event TouchActionEventHandler TouchAction;
8 |
9 | public TouchEffect() : base("CouchDraw.TouchEffect")
10 | { }
11 |
12 | public bool Capture { set; get; }
13 |
14 | public void OnTouchAction(Element element, TouchActionEventArgs args)
15 | {
16 | TouchAction?.Invoke(element, args);
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------