├── .gitignore ├── LICENSE ├── README.md └── src ├── TinyCache.BlobCache ├── BlobCacheStorage.cs └── TinyCache.BlobCache.csproj ├── TinyCache.FileStorage ├── FileStorage.cs └── TinyCache.FileStorage.csproj ├── TinyCache.Forms ├── Class1.cs ├── PreloadExtensions.cs ├── TinyCache.Forms.csproj └── XamarinPropertyStorage.cs ├── TinyCache.sln ├── TinyCache ├── CacheUpdatedEvt.cs ├── Interfaces │ ├── ICacheStorage.cs │ ├── IPreloadableCache.cs │ └── MemoryDictionaryCache.cs ├── TinyCache.cs ├── TinyCache.csproj ├── TinyCache.sln ├── TinyCacheDelegationHandler.cs ├── TinyCacheHandler.cs ├── TinyCacheModeEnum.cs ├── TinyCachePolicy.cs ├── TinyConnectivity.cs ├── TinyRetryDelegationHandler.cs └── build-log.txt ├── TinyControls.Drawer.Android ├── Properties │ └── AssemblyInfo.cs ├── TinyControls.Drawer.Android.csproj └── packages.config ├── TinyControls.Drawer.NuGet ├── TinyControls.Drawer.NuGet.nuget.props ├── TinyControls.Drawer.NuGet.nuget.targets └── TinyControls.Drawer.NuGet.nuproj ├── TinyControls.Drawer.Shared ├── DrawerControl.cs ├── DrawerTypes │ ├── OverlayType.cs │ └── ViewSizeArgs.cs ├── OverlayControl.cs ├── TinyControls.Drawer.Shared.projitems ├── TinyControls.Drawer.Shared.shproj └── ViewOverlay.cs ├── TinyControls.Drawer.iOS ├── Properties │ └── AssemblyInfo.cs ├── Renderers │ └── DrawerControlRenderer.cs ├── TinyControls.Drawer.iOS.csproj └── packages.config ├── TinyTranslations.Forms ├── TinyTranslations.Forms.csproj ├── TranslateExtension.cs └── TranslationHelper.cs ├── TinyTranslatons ├── TinyTranslations.csproj ├── TranslationClient.cs └── TranslationDictionary.cs └── samples └── Forms ├── Droid ├── Assets │ └── AboutAssets.txt ├── FodyWeavers.xml ├── MainActivity.cs ├── MainApplication.cs ├── Properties │ ├── AndroidManifest.xml │ └── AssemblyInfo.cs ├── Resources │ ├── AboutResources.txt │ ├── Resource.designer.cs │ ├── drawable-hdpi │ │ └── icon.png │ ├── drawable-xhdpi │ │ └── icon.png │ ├── drawable-xxhdpi │ │ └── icon.png │ ├── drawable │ │ └── icon.png │ ├── layout │ │ ├── Tabbar.axml │ │ └── Toolbar.axml │ └── values │ │ └── styles.xml ├── gymlocator.Droid.csproj └── packages.config ├── gymlocator.Core ├── CacheResources │ ├── PreloadData.cs │ └── reload.json ├── DataStore.cs ├── FodyWeavers.xml ├── Rest │ ├── GymAPI.cs │ ├── GymAPIExtensions.cs │ ├── IGymAPI.cs │ └── Models │ │ ├── Address.cs │ │ ├── ContactDetails.cs │ │ ├── Coordinates.cs │ │ ├── Error.cs │ │ ├── Feature.cs │ │ ├── GroupTrainingType.cs │ │ ├── Gym.cs │ │ ├── Image.cs │ │ ├── PersonalTrainer.cs │ │ ├── SOSChildrensVillage.cs │ │ └── StaffedHour.cs ├── RestAddons │ └── Gym.cs ├── ShoppingNet │ ├── IShoppingAPI.cs │ ├── Models │ │ ├── Item.cs │ │ └── ShoppingList.cs │ ├── ShoppingAPI.cs │ └── ShoppingAPIExtensions.cs └── gymlocator.Core.csproj ├── gymlocator ├── App.xaml ├── App.xaml.cs ├── Controls │ ├── DrawerControl.xaml │ ├── DrawerControl.xaml.cs │ ├── RepeaterView.cs │ ├── RoundedBoxView.cs │ └── SearchBox.cs ├── ViewModels │ ├── GymDetailViewModel.cs │ ├── GymViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── GymDetailView.xaml │ ├── GymDetailView.xaml.cs │ ├── GymListView.xaml │ └── GymListView.xaml.cs ├── gymlocator.projitems └── gymlocator.shproj └── iOS ├── AppDelegate.cs ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json └── Contents.json ├── Entitlements.plist ├── FodyWeavers.xml ├── Info.plist ├── LaunchScreen.storyboard ├── Main.cs ├── Renderers ├── CustomBoxViewRenderer.cs ├── CustomEntryRenderer.cs ├── LargeTabbedPageRenderer.cs └── TabbedPageRenderer.cs ├── gymlocator.iOS.csproj └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | # Autosave files 2 | *~ 3 | 4 | # build 5 | [Oo]bj/ 6 | [Bb]in/ 7 | packages/ 8 | TestResults/ 9 | 10 | # globs 11 | Makefile.in 12 | *.DS_Store 13 | *.sln.cache 14 | *.suo 15 | *.cache 16 | *.pidb 17 | *.userprefs 18 | *.usertasks 19 | config.log 20 | config.make 21 | config.status 22 | aclocal.m4 23 | install-sh 24 | autom4te.cache/ 25 | *.user 26 | *.tar.gz 27 | tarballs/ 28 | test-results/ 29 | Thumbs.db 30 | .vs/ 31 | 32 | # Mac bundle stuff 33 | *.dmg 34 | *.app 35 | 36 | # resharper 37 | *_Resharper.* 38 | *.Resharper 39 | 40 | # dotCover 41 | *.dotCover 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 TinyStuff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyCache 2 | Small helper for offline and caching of long-running processes 3 | 4 | ## Build status 5 | ![buildstatus](https://io2gamelabs.visualstudio.com/_apis/public/build/definitions/be16d002-5786-41a1-bf3b-3e13d5e80aa0/14/badge) 6 | 7 | ## Breaking changes in version 2.0 8 | Version 2.0.x will introduce breaking changes to how to use TinyCache. Pre 2.0 we could only have one instance of TinyCache. 2.0 introduces the option to have multiple instances of TinyCache. To make that possible we have introduced TinyCacheHandler. 9 | 10 | ## TinyCacheHandler 11 | If we only want one instance of TinyCache, we can use the **Default** method of **TinyCacheHandler**. First time you access the Default property a new instance of TinyCache will be created if there are not instances created. This instance will get **default** as the key. 12 | 13 | ### Create an additional instance of TinyCache 14 | To add a new instance of TinyCacheHandler we can use either the create method or the add method. 15 | 16 | 17 | **Using the Create method:** 18 | ```csharp 19 | var newCache = TinyCacheHandler.Create("myNewCache"); 20 | ``` 21 | 22 | **Using the Add method:** 23 | ```csharp 24 | var newCache = new TinyCache(); 25 | TinyCacheHandler.Add("myNewCache", newCache); 26 | ``` 27 | ### Set default cache 28 | If we have multiple cache instances, we maybe not want the first one to be default, then we can change that by passing the cahce key to the SetDefault method of TinyCacheHandler. 29 | 30 | ```csharp 31 | TinyCacheHandler.SetDefault("myNewCache"); 32 | ``` 33 | 34 | ### Get a specific instance of TinyCache 35 | If we want to get an instance of TinyCache that not are the default instance, we can use the key for the instance as in the code below. 36 | ```csharp 37 | var cache = TinyCacheHandler.Get("myNewCache"); 38 | 39 | var result = cache.RunAsync>("cachekey", () => { return api.GetData("customdata"); }); 40 | ``` 41 | 42 | ## Example 43 | 44 | ### Use file storage 45 | Install NuGet package TinyCache.FileStorage. 46 | 47 | ```csharp 48 | // Create a cache storage, in memory cache will be the default. 49 | var store = new FileStorage(); 50 | 51 | var cacheFolder = string.Empty; 52 | 53 | #if __IOS__ || __MACOS__ 54 | cacheFolder = NSSearchPath.GetDirectories(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomain.User)[0]; 55 | #elif __ANDROID__ 56 | cacheFolder = Application.Context.CacheDir.AbsolutePath; 57 | #elif __UWP__ 58 | cacheFolder = Windows.Storage.ApplicationData.Current.LocalFolder.Path; 59 | #else 60 | cacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 61 | #endif 62 | 63 | store.Initialize(cacheFolder); 64 | 65 | // Set cache storage 66 | TinyCache.TinyCache.SetCacheStore(store); 67 | 68 | // Fetch data with default policy 69 | var result = await TinyCacheHandler.Default.RunAsync>("cachekey", () => { return api.GetData("customdata"); }); 70 | ``` 71 | 72 | ### Use property storage in Xamarin.Forms 73 | Install NuGet package TinyCache.Forms. 74 | 75 | ```csharp 76 | // Create a cache storage, in memory cache will be the default. 77 | var store = new XamarinPropertyStorage(); 78 | 79 | // Set cache storage 80 | TinyCacheHandler.Default.SetCacheStore(store); 81 | 82 | // Fetch data with default policy 83 | var result = await TinyCacheHandler.Default..RunAsync>("cachekey", () => { return api.GetData("customdata"); }); 84 | 85 | ``` 86 | ## Caching DelegationHandler 87 | ```csharp 88 | AuoRestApi api = new AuoRestApi(apiEndPoint, new NoClientCredentials(), new TinyCache.TinyCacheDelegationHandler()); 89 | ``` 90 | ## Some extra examples 91 | Not needed, but nice to have 92 | ```csharp 93 | 94 | // Handle errors 95 | TinyCache.TinyCache.OnError += (sender, e) => 96 | { 97 | ShowError(e); 98 | }; 99 | 100 | // Set a base policy that will be used when no policy is specified 101 | TinyCacheHandler.Default..SetBasePolicy( 102 | new TinyCachePolicy() 103 | .SetMode(TinyCacheModeEnum.CacheFirst) // fetch from cache first 104 | .SetFetchTimeout(TimeSpan.FromSeconds(5)) // 5 second excecution limit 105 | .SetExpirationTime(TimeSpan.FromMinutes(10)) // 10 minute expiration before next fetch 106 | .SetUpdateCacheTimeout(50) // Wait 50ms before trying to update cache in background 107 | .UpdateHandler = async (key, newdata) => { await DoStuff(key, newdata); }); // Handle background updates 108 | 109 | // Handle background changes 110 | TinyCacheHandler.Default.OnUpdate += async (object sender, CacheUpdatedEvt e) => { 111 | var cacheKey = e.Key; 112 | var dataObject = e.Value; 113 | async HandleObjectChange(cacheKey,dataObject as MyDataType); 114 | }; 115 | 116 | ``` 117 | ## Some extra for preloading cache 118 | Not needed, but nice to have 119 | ```csharp 120 | 121 | // Get all cached data from storage (to be saved in static file in project and then loaded) 122 | var preloadString = store.GetAllAsLoadableString(); 123 | 124 | // Preload cache if needed from stored string: 125 | store.LoadFromString(CacheResources.PreloadData.JsonData); 126 | ``` 127 | -------------------------------------------------------------------------------- /src/TinyCache.BlobCache/BlobCacheStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Akavache; 3 | 4 | namespace TinyCacheLib.BlobCache 5 | { 6 | public class BlobCacheStorage : ICacheStorage 7 | { 8 | private IBlobCache _internalCache; 9 | 10 | public BlobCacheStorage(IBlobCache storage) 11 | { 12 | _internalCache = storage; 13 | } 14 | 15 | public object Get(string key, Type t) 16 | { 17 | return _internalCache.GetObject(key); 18 | } 19 | 20 | public void Remove(string key) 21 | { 22 | _internalCache.Invalidate(key); 23 | } 24 | 25 | public bool Store(string key, object value) 26 | { 27 | _internalCache.InsertObject(key, value, TimeSpan.FromDays(200)); 28 | return true; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/TinyCache.BlobCache/TinyCache.BlobCache.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.6 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/TinyCache.FileStorage/FileStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Text; 5 | using Newtonsoft.Json; 6 | 7 | namespace TinyCacheLib.FileStorage 8 | { 9 | public class FileStorage : ICacheStorage 10 | { 11 | private string cacheFolder; 12 | 13 | public void Initialize(string cacheFolder) 14 | { 15 | this.cacheFolder = cacheFolder; 16 | } 17 | 18 | public object Get(string key, Type t) 19 | { 20 | var path = GetPath(key); 21 | 22 | try 23 | { 24 | if (File.Exists(path)) 25 | { 26 | var json = File.ReadAllText(path); 27 | 28 | var result = JsonConvert.DeserializeObject(json, t); 29 | 30 | return result; 31 | } 32 | } 33 | catch (Exception ex) 34 | { 35 | return null; 36 | } 37 | return null; 38 | } 39 | 40 | public void Remove(string key) 41 | { 42 | var path = GetPath(key); 43 | 44 | if (File.Exists(path)) 45 | { 46 | File.Delete(path); 47 | } 48 | } 49 | 50 | public static object lockObj = 1; 51 | 52 | public bool Store(string key, object value, bool checkChange = true) 53 | { 54 | var hasChanged = true; 55 | 56 | var path = GetPath(key); 57 | string json = null; 58 | lock (lockObj) 59 | { 60 | json = JsonConvert.SerializeObject(value); 61 | } 62 | 63 | if (File.Exists(path)) 64 | { 65 | try 66 | { 67 | var content = File.ReadAllText(path); 68 | 69 | if (content == json) 70 | { 71 | hasChanged = false; 72 | } 73 | } 74 | catch (Exception ex) 75 | { 76 | return false; 77 | } 78 | } 79 | 80 | if (hasChanged) 81 | { 82 | try 83 | { 84 | File.WriteAllText(path, json, Encoding.UTF8); 85 | } 86 | catch (Exception ex) 87 | { 88 | return false; 89 | } 90 | } 91 | 92 | return hasChanged; 93 | } 94 | 95 | private string GetPath(string key) 96 | { 97 | if (string.IsNullOrWhiteSpace((cacheFolder))) 98 | { 99 | throw new Exception("Initialize has to be called before using TinyCache with file storage"); 100 | } 101 | 102 | var encoded = WebUtility.UrlEncode(key); 103 | 104 | var name = string.Format("TinyCache_{0}.cache", encoded); 105 | 106 | return Path.Combine(cacheFolder, name); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/TinyCache.FileStorage/TinyCache.FileStorage.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.4 5 | Small helper for offline and caching of long-running processes 6 | https://github.com/matst80/tinycache/LICENSE 7 | Git 8 | https://github.com/TinyStuff/TinyCache 9 | https://github.com/TinyStuff/TinyCache 10 | 11 | TinyCache.FileStorage 12 | Mats Törnberg 13 | 1.0.0 14 | false 15 | TinyCacheLib.FileStorage 16 | Copyright 2018 17 | https://i.imgur.com/rsc2LTX.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/TinyCache.Forms/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TinyCache.Forms 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/TinyCache.Forms/PreloadExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace TinyCacheLib.Forms 3 | { 4 | public static class PreloadExtensions 5 | { 6 | public static void LoadFromFile(this IPreloadableCache store, string filename) 7 | { 8 | var text = System.IO.File.ReadAllText(filename); 9 | store.LoadFromString(text); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/TinyCache.Forms/TinyCache.Forms.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.4 5 | Small helper for offline and caching of long-running processes 6 | https://github.com/matst80/tinycache/LICENSE 7 | Git 8 | https://github.com/TinyStuff/TinyCache 9 | https://github.com/TinyStuff/TinyCache 10 | 11 | TinyCache 12 | Mats Törnberg 13 | 1.0.0 14 | false 15 | TinyCacheLib.Forms 16 | Copyright 2018 17 | https://i.imgur.com/rsc2LTX.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/TinyCache.Forms/XamarinPropertyStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Xamarin.Forms; 5 | 6 | namespace TinyCacheLib 7 | { 8 | public class XamarinPropertyStorage : IPreloadableCache 9 | { 10 | private IDictionary AppStorage => Application.Current.Properties; 11 | 12 | public object Get(string key, Type t) 13 | { 14 | if (AppStorage.ContainsKey(key)) 15 | { 16 | var stringValue = AppStorage[key] as string; 17 | return JsonConvert.DeserializeObject(stringValue, t); 18 | } 19 | 20 | return null; 21 | } 22 | 23 | private object thisLock = new Object(); 24 | 25 | public bool Store(string key, object value, bool checkChange = true) 26 | { 27 | if (value == null) 28 | return false; 29 | 30 | var stringValue = JsonConvert.SerializeObject(value); 31 | var ret = false; 32 | 33 | if (!checkChange) 34 | { 35 | AppStorage[key] = stringValue; 36 | return true; 37 | } 38 | 39 | 40 | if (AppStorage != null && !string.IsNullOrEmpty(stringValue)) 41 | { 42 | lock (thisLock) 43 | { 44 | var hasChanged = false; 45 | if (AppStorage.ContainsKey(key)) 46 | { 47 | if (AppStorage[key] as string != stringValue) 48 | { 49 | AppStorage[key] = stringValue; 50 | hasChanged = true; 51 | } 52 | } 53 | else 54 | { 55 | AppStorage.Add(key, stringValue); 56 | ret = true; 57 | hasChanged = true; 58 | } 59 | if (hasChanged) 60 | Application.Current.SavePropertiesAsync(); 61 | } 62 | } 63 | 64 | return ret; 65 | } 66 | 67 | public void Remove(string key) 68 | { 69 | if (AppStorage != null && AppStorage.ContainsKey(key)) 70 | { 71 | AppStorage.Remove(key); 72 | } 73 | } 74 | 75 | public string GetAllAsLoadableString() 76 | { 77 | return JsonConvert.SerializeObject(AppStorage); 78 | } 79 | 80 | public void LoadFromString(string fillData) 81 | { 82 | var dict = JsonConvert.DeserializeObject>(fillData); 83 | 84 | foreach (var key in dict.Keys) 85 | { 86 | if (!AppStorage.ContainsKey(key)) 87 | { 88 | AppStorage.Add(key, dict[key]); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/TinyCache/CacheUpdatedEvt.cs: -------------------------------------------------------------------------------- 1 | namespace TinyCacheLib 2 | { 3 | public class CacheUpdatedEvt 4 | { 5 | public CacheUpdatedEvt() { } 6 | 7 | public CacheUpdatedEvt(string key, object value) 8 | { 9 | Key = key; 10 | Value = value; 11 | } 12 | 13 | public string Key { get; set; } 14 | 15 | public object Value { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/TinyCache/Interfaces/ICacheStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TinyCacheLib 4 | { 5 | public interface ICacheStorage 6 | { 7 | bool Store(string key, object value, bool checkChange = true); 8 | object Get(string key, Type t); 9 | void Remove(string key); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/TinyCache/Interfaces/IPreloadableCache.cs: -------------------------------------------------------------------------------- 1 | namespace TinyCacheLib 2 | { 3 | public interface IPreloadableCache : ICacheStorage 4 | { 5 | string GetAllAsLoadableString(); 6 | void LoadFromString(string fillData); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/TinyCache/Interfaces/MemoryDictionaryCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace TinyCacheLib 5 | { 6 | public class MemoryDictionaryCache : ICacheStorage 7 | { 8 | private Dictionary cacheObj = new Dictionary(); 9 | 10 | public object Get(string key, Type t) 11 | { 12 | if (cacheObj.ContainsKey(key)) 13 | { 14 | return cacheObj[key]; 15 | } 16 | 17 | return null; 18 | } 19 | 20 | public void Remove(string key) 21 | { 22 | if (cacheObj.ContainsKey(key)) 23 | { 24 | cacheObj.Remove(key); 25 | } 26 | } 27 | 28 | public bool Store(string key, object value, bool checkChange = true) 29 | { 30 | var hasChanged = false; 31 | 32 | if (checkChange && cacheObj.ContainsKey(key)) 33 | { 34 | hasChanged = cacheObj[key] != value; 35 | cacheObj[key] = value; 36 | } 37 | else 38 | { 39 | cacheObj.Add(key, value); 40 | hasChanged = true; 41 | } 42 | 43 | return hasChanged; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/TinyCache/TinyCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace TinyCacheLib 7 | { 8 | 9 | public class TinyCache 10 | { 11 | private TinyCachePolicy defaultPolicy = new TinyCachePolicy(); 12 | 13 | public ICacheStorage Storage { get; internal set; } = new MemoryDictionaryCache(); 14 | public ICacheStorage SecondaryStorage { get; internal set; } 15 | 16 | public EventHandler OnError; 17 | public EventHandler OnUpdate; 18 | public EventHandler OnRemove; 19 | public EventHandler OnLoadingChange; 20 | 21 | /// 22 | /// Timeouts function after specified amout of milliseconds. 23 | /// 24 | /// Result of running function. 25 | /// Task to run. 26 | /// Timeout in milliseconds 27 | /// Type of result when running function 28 | public async Task TimeoutAfter(Func> task, double timeout) 29 | { 30 | OnLoadingChange?.Invoke(task, true); 31 | 32 | using (var timeoutCancellationTokenSource = new CancellationTokenSource()) 33 | { 34 | var tsk = task(); 35 | var completedTask = await Task.WhenAny(tsk, Task.Delay((int)timeout, timeoutCancellationTokenSource.Token)); 36 | if (completedTask == tsk) 37 | { 38 | OnLoadingChange?.Invoke(task, false); 39 | timeoutCancellationTokenSource.Cancel(); 40 | return await tsk; // Very important in order to propagate exceptions 41 | } 42 | 43 | OnLoadingChange?.Invoke(task, false); 44 | throw new TimeoutException("The operation has timed out."); 45 | } 46 | } 47 | 48 | public void Store(string key, object data) 49 | { 50 | if (string.IsNullOrEmpty(key)) 51 | throw new ArgumentNullException(nameof(key)); 52 | if (data != null) 53 | { 54 | Storage.Store(key, data); 55 | if (SecondaryStorage != null) 56 | { 57 | SecondaryStorage.Store(key, data); 58 | } 59 | } 60 | } 61 | 62 | /// 63 | /// Gets from cache storage and return null if not found. 64 | /// 65 | /// The from storage. 66 | /// Cache key. 67 | /// Type of the stored data. 68 | public T GetFromStorage(string key) 69 | { 70 | var genericType = typeof(T); 71 | object ret = Storage.Get(key, genericType); 72 | if (ret == null && SecondaryStorage != null) 73 | { 74 | ret = SecondaryStorage.Get(key, genericType); 75 | if (ret != null) 76 | Storage.Store(key, ret); 77 | } 78 | return (T)ret; 79 | } 80 | 81 | /// 82 | /// Fetch using policy 83 | /// 84 | /// The data from function or cache depending on policy. 85 | /// Cache key. 86 | /// Function for populating cache 87 | /// Policy. 88 | /// Method to call when data is updated in the background. 89 | /// Return type of function and cache object. 90 | public async Task RunAsync(string key, Func> func, TinyCachePolicy policy = null, Action onUpdate = null) 91 | { 92 | var genericType = typeof(T); 93 | object ret = Storage.Get(key, genericType); 94 | if (ret == null && SecondaryStorage != null) 95 | { 96 | ret = SecondaryStorage.Get(key, genericType); 97 | } 98 | policy = policy ?? defaultPolicy; 99 | 100 | if (ret == null) { 101 | ret = await func(); 102 | Store(key, ret, policy); 103 | } 104 | else if (policy.UseCacheFirstFunction == null || !policy.UseCacheFirstFunction()) 105 | { 106 | if (policy.Mode == TinyCacheModeEnum.FetchFirst) 107 | { 108 | try 109 | { 110 | var realFetch = await TimeoutAfter(func, policy.FetchTimeout); 111 | if (!(realFetch is T)) 112 | { 113 | ret = realFetch; 114 | Store(key, realFetch, policy); 115 | } 116 | } 117 | catch (Exception ex) 118 | { 119 | OnError?.Invoke(policy, ex); 120 | policy.ExceptionHandler?.Invoke(ex, true); 121 | } 122 | } 123 | } 124 | StartBackgroundFetch(key, func, policy, onUpdate); 125 | AddLastFetch(key); 126 | return (ret == null) ? default(T) : (T)ret; 127 | } 128 | 129 | /// 130 | /// Sets the base policy wich will be used if not specified in each request. 131 | /// 132 | /// Tiny cache policy. 133 | public void SetBasePolicy(TinyCachePolicy tinyCachePolicy) 134 | { 135 | defaultPolicy = tinyCachePolicy; 136 | } 137 | 138 | /// 139 | /// Sets the permanent (secondary) cache storage type. 140 | /// 141 | /// Storage instance. 142 | public void SetCacheStore(ICacheStorage store) 143 | { 144 | SecondaryStorage = store; 145 | } 146 | 147 | /// 148 | /// Sets the permanent cache storage type. 149 | /// 150 | /// Storage instance. 151 | public void SetCacheStore(ICacheStorage primary, ICacheStorage secondary) 152 | { 153 | Storage = primary; 154 | SecondaryStorage = secondary; 155 | } 156 | 157 | /// 158 | /// Remove the specified key from the cache store 159 | /// 160 | /// The remove. 161 | /// Key. 162 | public void Remove(string key) 163 | { 164 | Storage.Remove(key); 165 | OnRemove?.Invoke(key, new CacheUpdatedEvt() 166 | { 167 | Key = key, 168 | Value = null 169 | }); 170 | } 171 | 172 | private void StartBackgroundFetch(string key, Func> func, TinyCachePolicy policy, Action onUpdate) 173 | { 174 | if (policy.UpdateCacheTimeout > 0) 175 | { 176 | if (ShouldFetch(policy.UpdateCacheTimeout, key)) 177 | { 178 | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed 179 | Task.Delay((int)policy.UpdateCacheTimeout).ContinueWith(async (arg) => 180 | { 181 | try 182 | { 183 | var newvalue = await TimeoutAfter(func, policy.BackgroundFetchTimeout); 184 | onUpdate?.Invoke(newvalue); 185 | Store(key, newvalue, policy); 186 | } 187 | catch (Exception ex) 188 | { 189 | if (policy.ReportExceptionsOnBackgroundFetch) 190 | { 191 | OnError?.Invoke(policy, ex); 192 | policy.ExceptionHandler?.Invoke(ex, true); 193 | } 194 | } 195 | }); 196 | } 197 | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed 198 | } 199 | } 200 | 201 | private Object thisLock = new Object(); 202 | private void AddLastFetch(string key) 203 | { 204 | lock (thisLock) 205 | { 206 | if (lastFetch.ContainsKey(key)) 207 | { 208 | lastFetch[key] = DateTime.Now; 209 | } 210 | else 211 | { 212 | lastFetch.Add(key, DateTime.Now); 213 | } 214 | } 215 | } 216 | 217 | private bool ShouldFetch(double v, string key) 218 | { 219 | if (!lastFetch.ContainsKey(key)) 220 | { 221 | return true; 222 | } 223 | 224 | var timeDiff = (DateTime.Now - lastFetch[key]).TotalMilliseconds; 225 | return (timeDiff > v); 226 | } 227 | 228 | private void Store(string key, object val, TinyCachePolicy policy) 229 | { 230 | if (val != null) 231 | { 232 | AddLastFetch(key); 233 | 234 | if (Storage.Store(key, val)) 235 | { 236 | policy?.UpdateHandler?.Invoke(key, val); 237 | OnUpdate?.Invoke(val, new CacheUpdatedEvt(key, val)); 238 | if (SecondaryStorage != null) 239 | { 240 | Task.Delay(10).ContinueWith((a) => 241 | { 242 | SecondaryStorage.Store(key, val, false); 243 | }); 244 | } 245 | } 246 | } 247 | } 248 | 249 | private Dictionary lastFetch = new Dictionary(); 250 | 251 | 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/TinyCache/TinyCache.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.4 5 | Small helper for offline and caching of long-running processes 6 | https://github.com/matst80/tinycache/LICENSE 7 | Git 8 | https://github.com/TinyStuff/TinyCache 9 | https://github.com/TinyStuff/TinyCache 10 | 11 | TinyCache 12 | Mats Törnberg 13 | 1.0.0 14 | false 15 | TinyCacheLib 16 | Copyright 2018 17 | https://i.imgur.com/rsc2LTX.png 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/TinyCache/TinyCache.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2017 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyCache", "TinyCache.csproj", "{050764D7-0E93-4909-B729-9F54C38D2EFB}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {050764D7-0E93-4909-B729-9F54C38D2EFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {050764D7-0E93-4909-B729-9F54C38D2EFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {050764D7-0E93-4909-B729-9F54C38D2EFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {050764D7-0E93-4909-B729-9F54C38D2EFB}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /src/TinyCache/TinyCacheDelegationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace TinyCacheLib 9 | { 10 | public class TinyCacheDelegationHandler : DelegatingHandler 11 | { 12 | private readonly TinyCache cache; 13 | 14 | public TinyCacheDelegationHandler(TinyCache cache) 15 | { 16 | this.cache = cache; 17 | } 18 | 19 | public TinyCachePolicy DefaultPolicy { get; set; } 20 | 21 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 22 | { 23 | if (request.Method == HttpMethod.Get) 24 | { 25 | return await cache.RunAsync(request.RequestUri.ToString(), async () => 26 | { 27 | var ret = await base.SendAsync(request, cancellationToken); 28 | return ret; 29 | }, DefaultPolicy); 30 | } 31 | 32 | return await base.SendAsync(request, cancellationToken); 33 | } 34 | } 35 | 36 | internal class ResponseHandler 37 | { 38 | internal ResponseHandler(Task originalRequest) 39 | { 40 | IsRunning = !originalRequest.IsCompleted; 41 | HandleRequest(originalRequest); 42 | 43 | } 44 | 45 | private void HandleRequest(Task originalRequest) 46 | { 47 | var waiter = originalRequest.GetAwaiter(); 48 | waiter.OnCompleted(() => 49 | { 50 | IsRunning = false; 51 | OriginalRespone = originalRequest.Result; 52 | OnCompleted?.Invoke(this, new EventArgs()); 53 | }); 54 | } 55 | 56 | public bool IsRunning { get; set; } 57 | private HttpResponseMessage OriginalRespone { get; set; } 58 | 59 | public EventHandler OnCompleted { get; set; } 60 | 61 | private HttpResponseMessage CloneResponse(HttpResponseMessage response) 62 | { 63 | var newResponse = new HttpResponseMessage(response.StatusCode); 64 | var ms = new MemoryStream(); 65 | 66 | foreach (var v in response.Headers) newResponse.Headers.TryAddWithoutValidation(v.Key, v.Value); 67 | if (response.Content != null) 68 | { 69 | response.Content.CopyToAsync(ms).GetAwaiter().GetResult(); 70 | ms.Position = 0; 71 | newResponse.Content = new StreamContent(ms); 72 | 73 | foreach (var v in response.Content.Headers) newResponse.Content.Headers.TryAddWithoutValidation(v.Key, v.Value); 74 | 75 | } 76 | return newResponse; 77 | } 78 | 79 | public Task AddRequest() 80 | { 81 | var task = new Task(() => 82 | { 83 | while (IsRunning) 84 | { 85 | Task.Delay(30); 86 | } 87 | return CloneResponse(OriginalRespone); 88 | }); 89 | task.Start(); 90 | return task; 91 | } 92 | } 93 | 94 | public class TinyNetworkDispatcher : DelegatingHandler 95 | { 96 | private Dictionary currentRequests = new Dictionary(); 97 | 98 | private object lockObj = true; 99 | 100 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 101 | { 102 | if (request.Method == HttpMethod.Get) 103 | { 104 | var key = request.RequestUri.PathAndQuery; 105 | lock (lockObj) 106 | { 107 | var hasHandler = currentRequests.ContainsKey(key); 108 | if (hasHandler && currentRequests[key].IsRunning) 109 | { 110 | return currentRequests[key].AddRequest(); 111 | } 112 | else 113 | { 114 | var res = base.SendAsync(request, cancellationToken); 115 | var respHandler = new ResponseHandler(res); 116 | respHandler.OnCompleted += (sender, e) => 117 | { 118 | lock (lockObj) 119 | { 120 | currentRequests.Remove(key); 121 | } 122 | }; 123 | currentRequests.Add(key, respHandler); 124 | return res; 125 | } 126 | } 127 | } 128 | return base.SendAsync(request, cancellationToken); 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /src/TinyCache/TinyCacheHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TinyCacheLib 6 | { 7 | public static class TinyCacheHandler 8 | { 9 | private static readonly Dictionary caches = new Dictionary(); 10 | 11 | public static TinyCache Create(string key) 12 | { 13 | var cache = new TinyCache(); 14 | 15 | Add(key, cache); 16 | 17 | return cache; 18 | } 19 | 20 | public static void Add(string key, TinyCache cache) 21 | { 22 | if (caches.ContainsKey(key)) 23 | { 24 | caches[key] = cache; 25 | } 26 | else 27 | { 28 | caches.Add(key, cache); 29 | } 30 | 31 | if (caches.Count == 1) 32 | { 33 | DefaultKey = key; 34 | } 35 | } 36 | 37 | public static TinyCache Get(string key) 38 | { 39 | return caches[key]; 40 | } 41 | 42 | public static void Remove(string key) 43 | { 44 | caches.Remove(key); 45 | } 46 | 47 | public static void SetDefault(string key) 48 | { 49 | if (caches.ContainsKey(key)) 50 | { 51 | DefaultKey = key; 52 | } 53 | else 54 | { 55 | throw new KeyNotFoundException(); 56 | } 57 | } 58 | 59 | public static IEnumerable AllKeys => caches.Keys; 60 | public static string DefaultKey { get; private set; } 61 | 62 | public static TinyCache Default => caches.Any() ? caches[DefaultKey] : Create("default"); 63 | 64 | public static int Count => caches.Count; 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/TinyCache/TinyCacheModeEnum.cs: -------------------------------------------------------------------------------- 1 | namespace TinyCacheLib 2 | { 3 | public enum TinyCacheModeEnum 4 | { 5 | CacheFirst, 6 | FetchFirst, 7 | FetchWithoutStore, 8 | FromCacheWithoutFetch 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/TinyCache/TinyCachePolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TinyCacheLib 4 | { 5 | public class TinyCachePolicy 6 | { 7 | public virtual double ExpirationTimeoutInSeconds { get; set; } = 30; 8 | public virtual double FetchTimeout { get; set; } = 5000; 9 | public virtual double UpdateCacheTimeout { get; set; } = 0; 10 | public virtual bool UseNetworkResponseFirst { get; set; } 11 | public virtual TinyCacheModeEnum Mode { get; set; } = TinyCacheModeEnum.CacheFirst; 12 | public double BackgroundFetchTimeout { get; set; } = 20000; 13 | public bool ReportExceptionsOnBackgroundFetch { get; set; } 14 | 15 | public Func UseCacheFirstFunction { get; set; } 16 | 17 | public Action ExceptionHandler { get; set; } 18 | public Action UpdateHandler { get; set; } 19 | 20 | public TinyCachePolicy SetUpdateCacheTimeout(TimeSpan ts) 21 | { 22 | UpdateCacheTimeout = ts.TotalMilliseconds; 23 | return this; 24 | } 25 | 26 | public TinyCachePolicy SetUpdateCacheTimeout(double milliseconds) 27 | { 28 | UpdateCacheTimeout = milliseconds; 29 | return this; 30 | } 31 | 32 | public TinyCachePolicy SetMode(TinyCacheModeEnum mode) 33 | { 34 | Mode = mode; 35 | return this; 36 | } 37 | 38 | public TinyCachePolicy SetFetchTimeout(int timeoutInMilliseconds) 39 | { 40 | FetchTimeout = timeoutInMilliseconds; 41 | return this; 42 | } 43 | 44 | public TinyCachePolicy SetFetchTimeout(TimeSpan ts) 45 | { 46 | FetchTimeout = ts.TotalMilliseconds; 47 | return this; 48 | } 49 | 50 | public TinyCachePolicy SetBackgroundFetchTimeout(int timeoutInMilliseconds) 51 | { 52 | BackgroundFetchTimeout = timeoutInMilliseconds; 53 | return this; 54 | } 55 | 56 | public TinyCachePolicy SetExpirationTime(TimeSpan ts) 57 | { 58 | ExpirationTimeoutInSeconds = ts.TotalSeconds; 59 | return this; 60 | } 61 | 62 | public TinyCachePolicy SetExpirationTime(double seconds) 63 | { 64 | ExpirationTimeoutInSeconds = seconds; 65 | return this; 66 | } 67 | 68 | public TinyCachePolicy SetBackgroundFetchTimeout(TimeSpan ts) 69 | { 70 | BackgroundFetchTimeout = ts.TotalMilliseconds; 71 | return this; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/TinyCache/TinyConnectivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace TinyCacheLib 7 | { 8 | public class TinyConnectivity 9 | { 10 | 11 | private static TinyConnectivity _instance; 12 | public static TinyConnectivity Instance 13 | { 14 | get 15 | { 16 | return _instance ?? (_instance = new TinyConnectivity()); 17 | } 18 | set { 19 | _instance = value; 20 | } 21 | } 22 | 23 | public static bool HasInternet 24 | { 25 | get; set; 26 | } 27 | 28 | private bool _seemConnected; 29 | public bool IsConnected 30 | { 31 | get 32 | { 33 | Task.Run(async () => await Ping()); 34 | return _seemConnected; 35 | } 36 | set 37 | { 38 | _seemConnected = value; 39 | } 40 | } 41 | 42 | public TimeSpan CheckTimeSpan { get; private set; } = TimeSpan.FromSeconds(5); 43 | public string PingUrl { get; private set; } = "http://www.google.com/"; 44 | 45 | private HttpClient _client; 46 | private DateTime lastCheck; 47 | 48 | private async Task Ping() 49 | { 50 | if ((DateTime.Now - lastCheck) > CheckTimeSpan) 51 | { 52 | lastCheck = DateTime.Now; 53 | if (_client == null) 54 | { 55 | _client = new HttpClient(); 56 | } 57 | var request = new HttpRequestMessage(HttpMethod.Head, PingUrl); 58 | 59 | var timeout = _seemConnected ? 150 : 9000; 60 | _seemConnected = false; 61 | 62 | using (var timeoutCancellationTokenSource = new CancellationTokenSource()) 63 | { 64 | try 65 | { 66 | var tsk = _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, timeoutCancellationTokenSource.Token); 67 | var completedTask = await Task.WhenAny(tsk, Task.Delay(timeout, timeoutCancellationTokenSource.Token)); 68 | if (completedTask == tsk) 69 | { 70 | _seemConnected = true; 71 | } 72 | lastCheck = DateTime.Now; 73 | } 74 | catch (Exception ex) 75 | { 76 | 77 | } 78 | } 79 | } 80 | if (_seemConnected) 81 | TinyConnectivity.HasInternet = true; 82 | return _seemConnected; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/TinyCache/TinyRetryDelegationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace TinyCacheLib 8 | { 9 | public class TinyRetryDelegationHandler : DelegatingHandler 10 | { 11 | 12 | public IList ExceptionForRetry { get; set; } = new List(); 13 | public int MaxRetrys { get; private set; } = 5; 14 | public int DelayBetweenRetries { get; set; } = 500; 15 | 16 | public void RetryOn() where T : Exception 17 | { 18 | ExceptionForRetry.Add(typeof(T)); 19 | } 20 | 21 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 22 | { 23 | int retrys = 0; 24 | var lastException = new Exception("Unknown error in retryhandler"); 25 | while (retrys++ <= MaxRetrys) 26 | { 27 | try 28 | { 29 | if (retrys > 1) 30 | request.Headers.Add("x-retry", retrys.ToString()); 31 | var ret = await base.SendAsync(request, cancellationToken); 32 | return ret; 33 | } 34 | catch (Exception ex) 35 | { 36 | lastException = ex; 37 | var doRetry = false; 38 | foreach (var t in ExceptionForRetry) 39 | { 40 | if (ex.GetType() == t) 41 | doRetry = true; 42 | } 43 | if (doRetry) 44 | await Task.Delay(DelayBetweenRetries); 45 | else 46 | throw ex; 47 | } 48 | } 49 | throw lastException; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Android/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("TinyControls.Drawer.Android")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.0.0")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Android/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.NuGet/TinyControls.Drawer.NuGet.nuget.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | /Users/dhindrik/.nuget/packages/ 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.NuGet/TinyControls.Drawer.NuGet.nuget.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | /Users/dhindrik/.nuget/packages/ 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.NuGet/TinyControls.Drawer.NuGet.nuproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B45DDBCA-709E-4AE1-9554-53A74469E773} 8 | TinyControls.Drawer 9 | TinyControls.Drawer 10 | 1.0.0 11 | mats 12 | false 13 | false 14 | Exe 15 | TinyControls.Drawer 16 | false 17 | TinyControls.Drawer.NuGet 18 | v4.5 19 | 20 | 21 | true 22 | full 23 | bin\Debug 24 | prompt 25 | 26 | 27 | bin\Release 28 | prompt 29 | 30 | 31 | 32 | {0497EACB-C129-438F-8DF9-9B100FD1EA44} 33 | TinyControls.Drawer.Android 34 | 35 | 36 | {95B3D2D0-737F-4080-8563-825EFA7E27EF} 37 | TinyControls.Drawer.iOS 38 | 39 | 40 | 41 | 42 | 0.2.0 43 | All 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/DrawerControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xamarin.Forms; 3 | 4 | namespace TinyControls 5 | { 6 | public class DrawerControl : ViewOverlay 7 | { 8 | public static readonly BindableProperty BackgroundOpacityProperty = 9 | BindableProperty.Create("BackgroundOpacity", typeof(double), typeof(DrawerControl), 0.0, validateValue: IsValidOpacity); 10 | 11 | public double BackgroundOpacity 12 | { 13 | get 14 | { 15 | return (double)GetValue(BackgroundOpacityProperty); 16 | } 17 | set 18 | { 19 | SetValue(BackgroundOpacityProperty, value); 20 | } 21 | } 22 | 23 | static bool IsValidOpacity(BindableObject bindable, object value) 24 | { 25 | double result; 26 | bool isDouble = double.TryParse(value.ToString(), out result); 27 | return isDouble && (result >= 0 && result <= 1); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/DrawerTypes/OverlayType.cs: -------------------------------------------------------------------------------- 1 | namespace TinyControls.Drawer 2 | 3 | { 4 | public enum OverlayType 5 | { 6 | Top, 7 | Bottom, 8 | Left, 9 | Right, 10 | Dialog 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/DrawerTypes/ViewSizeArgs.cs: -------------------------------------------------------------------------------- 1 | namespace TinyControls.Drawer 2 | { 3 | public class ViewSizeArgs 4 | { 5 | public double Old { get; set; } 6 | public double New { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/OverlayControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using TinyControls.Drawer; 6 | using Xamarin.Forms; 7 | 8 | namespace TinyControls 9 | { 10 | public class OverlayControl : Layout 11 | { 12 | public List Overlays { get; internal set; } = new List(); 13 | 14 | protected override void OnAdded(View view) 15 | { 16 | base.OnAdded(view); 17 | } 18 | 19 | protected override void OnChildAdded(Element child) 20 | { 21 | base.OnChildAdded(child); 22 | if (child is ViewOverlay overlay) 23 | { 24 | AddOverlay(overlay); 25 | } 26 | } 27 | 28 | public void AddOverlay(ViewOverlay overlay) 29 | { 30 | Overlays.Add(overlay); 31 | 32 | if (overlay.ShadowView != null) 33 | { 34 | overlay.UseShadow = true; 35 | overlay.ShadowView.IsEnabled = false; 36 | overlay.ShadowView.Opacity = 0; 37 | 38 | // Ugly as piiip.... 39 | Device.BeginInvokeOnMainThread(() => 40 | { 41 | if (overlay.ShadowView != null) 42 | { 43 | Children.Add(overlay.ShadowView); 44 | RaiseChild(overlay); 45 | } 46 | }); 47 | } 48 | 49 | 50 | 51 | var gest = new PanGestureRecognizer(); 52 | gest.PanUpdated += (sender, e) => 53 | { 54 | switch (e.StatusType) 55 | { 56 | case GestureStatus.Started: 57 | overlay.active = true; 58 | break; 59 | case GestureStatus.Running: 60 | overlay.lastOffset = overlay.offset; 61 | overlay.offset = overlay.IsHorizontal ? e.TotalX : e.TotalY; 62 | break; 63 | case GestureStatus.Completed: 64 | overlay.active = false; 65 | break; 66 | } 67 | UpdateLayout(overlay); 68 | }; 69 | overlay.GestureRecognizers.Add(gest); 70 | ForceLayout(); 71 | } 72 | 73 | private void UpdateLayout(ViewOverlay overlay) 74 | { 75 | var orgSize = overlay.OverlayBounds.Height - overlay.OverlayBounds.Y; 76 | var maxValue = TotalHeight; 77 | switch (overlay.Type) 78 | { 79 | case OverlayType.Top: 80 | orgSize = -overlay.OverlayBounds.Y; 81 | break; 82 | case OverlayType.Left: 83 | orgSize = -overlay.OverlayBounds.X; 84 | maxValue = TotalWidth; 85 | break; 86 | case OverlayType.Right: 87 | orgSize = overlay.OverlayBounds.Width - overlay.OverlayBounds.X; 88 | maxValue = TotalWidth; 89 | break; 90 | } 91 | var maxAllowed = Math.Min(maxValue, overlay.MaxSize); 92 | var newSize = (orgSize - overlay.offset); 93 | 94 | var backgroundOpacity = GetBackgroundOpacity(maxAllowed, newSize); 95 | if (overlay.active) 96 | { 97 | if (overlay.UseShadow) 98 | { 99 | overlay.ShadowView.Opacity = backgroundOpacity * 0.5f; 100 | if (overlay is DrawerControl dc) 101 | { 102 | dc.BackgroundOpacity = backgroundOpacity; 103 | } 104 | } 105 | overlay.Layout(GetRect(overlay, newSize, orgSize)); 106 | } 107 | else 108 | { 109 | var delta = overlay.lastOffset - overlay.offset; 110 | var absDelta = Math.Abs(delta); 111 | #if DEBUG 112 | Console.WriteLine("Drag delta: " + delta); 113 | #endif 114 | if (absDelta > 14) 115 | { 116 | newSize = (delta > 0) ? maxValue : overlay.MinSize; 117 | } 118 | else if (absDelta > 4) 119 | { 120 | newSize += (delta * 4); 121 | } 122 | var rect = GetRect(overlay, newSize, orgSize); 123 | backgroundOpacity = GetBackgroundOpacity(maxAllowed, newSize); 124 | overlay.LayoutTo(rect, 300, overlay.Easing); 125 | if (overlay.UseShadow) 126 | { 127 | overlay.ShadowView.FadeTo(backgroundOpacity * 0.5f, 300, Easing.Linear); 128 | if (overlay is DrawerControl dc) 129 | { 130 | dc.BackgroundOpacity = backgroundOpacity; 131 | } 132 | } 133 | overlay.OverlayBounds = rect; 134 | overlay.offset = 0; 135 | } 136 | } 137 | 138 | private static double GetBackgroundOpacity(double maxAllowed, double newSize) 139 | { 140 | return Math.Min(1, Math.Max(0, (newSize / maxAllowed))); 141 | } 142 | 143 | private void SetOverlaySize(ViewOverlay ov, double ns) 144 | { 145 | if (!ov.active) 146 | { 147 | //ov.Bounds = GetRect(ov, ns); 148 | ov.offset = -ns; 149 | UpdateLayout(ov); 150 | } 151 | } 152 | 153 | public void Rezise(ViewOverlay ov, float percent) 154 | { 155 | SetOverlaySize(ov, TotalHeight * (percent / 100f)); 156 | } 157 | 158 | public void Minimize(ViewOverlay sliderOverlay) 159 | { 160 | SetOverlaySize(sliderOverlay, 0); 161 | } 162 | 163 | public void MaximizeOverlay(ViewOverlay ov) 164 | { 165 | SetOverlaySize(ov, 99999); 166 | } 167 | 168 | private Rectangle GetRect(ViewOverlay overlay, double newSize, double oldSize = -1) 169 | { 170 | var x = overlay.OverlayBounds.X; 171 | var y = overlay.OverlayBounds.Y; 172 | var w = overlay.OverlayBounds.Width; 173 | var h = overlay.OverlayBounds.Height; 174 | 175 | var maxValue = TotalHeight; 176 | if (overlay.IsHorizontal) 177 | maxValue = TotalWidth; 178 | 179 | var maxAllowed = Math.Min(maxValue, overlay.MaxSize); 180 | 181 | if (newSize <= overlay.MinSize) 182 | newSize = overlay.MinSize; 183 | if (newSize >= maxAllowed) 184 | newSize = maxAllowed; 185 | 186 | if (oldSize > -1) 187 | { 188 | if (overlay.OnSizeChange != null) 189 | { 190 | overlay.OnSizeChange.Invoke(this, new ViewSizeArgs() 191 | { 192 | Old = oldSize, 193 | New = newSize 194 | }); 195 | } 196 | } 197 | switch (overlay.Type) 198 | { 199 | case OverlayType.Bottom: 200 | y = maxValue - newSize; 201 | break; 202 | case OverlayType.Top: 203 | y = -newSize; 204 | break; 205 | case OverlayType.Left: 206 | x = -newSize; 207 | break; 208 | case OverlayType.Right: 209 | x = maxValue - newSize; 210 | break; 211 | } 212 | 213 | return new Rectangle(x, y, w, h); 214 | } 215 | 216 | private double TotalWidth; 217 | private double TotalHeight; 218 | 219 | protected override void LayoutChildren(double x, double y, double width, double height) 220 | { 221 | TotalWidth = width; 222 | TotalHeight = height; 223 | var fullRect = new Rectangle(x, y, width, height); 224 | foreach (var child in Children) 225 | { 226 | if (!Overlays.Any(d => d == child)) 227 | child.Layout(fullRect); 228 | } 229 | foreach (var overlay in Overlays) 230 | { 231 | if (overlay.OverlayBounds.IsEmpty) 232 | { 233 | overlay.OverlayBounds = GetInitialBounds(overlay, fullRect); 234 | } 235 | overlay.Layout(overlay.OverlayBounds); 236 | } 237 | } 238 | 239 | private Rectangle GetInitialBounds(ViewOverlay overlay, Rectangle fullRect) 240 | { 241 | var initValue = Math.Max(overlay.MinSize, overlay.InitialSize); 242 | overlay.OverlayBounds = fullRect; 243 | return GetRect(overlay, initValue); 244 | } 245 | 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/TinyControls.Drawer.Shared.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | {D4DE07D9-658B-4399-BA85-827A29F083ED} 7 | 8 | 9 | TinyControls.Drawer 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/TinyControls.Drawer.Shared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {D4DE07D9-658B-4399-BA85-827A29F083ED} 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.Shared/ViewOverlay.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using TinyControls.Drawer; 3 | using Xamarin.Forms; 4 | 5 | namespace TinyControls 6 | { 7 | public class ViewOverlay : ContentView 8 | { 9 | 10 | public EventHandler OnSizeChange; 11 | public View ShadowView { get; set; } 12 | public OverlayType Type { get; set; } = OverlayType.Bottom; 13 | public double MinSize { get; set; } = 55; 14 | public double MaxSize { get; set; } = 99999; 15 | public double InitialSize { get; set; } 16 | internal bool UseShadow { get; set; } 17 | public bool Active { get; set; } 18 | internal Rectangle OverlayBounds { get; set; } 19 | public Easing Easing { get; set; } = Easing.SpringOut; 20 | public bool IsHorizontal 21 | { 22 | get 23 | { 24 | return Type == OverlayType.Left || Type == OverlayType.Right; 25 | } 26 | } 27 | 28 | internal double offset { get; set; } 29 | internal bool active { get; set; } 30 | internal double lastOffset { get; set; } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.iOS/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("TinyControls.Drawer.iOS")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.0.0")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.iOS/Renderers/DrawerControlRenderer.cs: -------------------------------------------------------------------------------- 1 | using CoreAnimation; 2 | using CoreGraphics; 3 | using TinyControls; 4 | using TinyControls.Drawer.iOS.Renderers; 5 | using UIKit; 6 | using Xamarin.Forms; 7 | using Xamarin.Forms.Platform.iOS; 8 | 9 | [assembly: ExportRenderer(typeof(DrawerControl), typeof(DrawerControlRenderer))] 10 | 11 | namespace TinyControls.Drawer.iOS.Renderers 12 | { 13 | public class DrawerControlRenderer : ViewRenderer 14 | { 15 | private readonly UIVisualEffectView visualEffectView; 16 | private readonly UIBlurEffect blur; 17 | 18 | public DrawerControlRenderer() 19 | { 20 | blur = UIBlurEffect.FromStyle(UIBlurEffectStyle.Regular); 21 | visualEffectView = new UIVisualEffectView(blur); 22 | visualEffectView.TintColor = UIColor.White; 23 | visualEffectView.Layer.MasksToBounds = true; 24 | visualEffectView.Layer.CornerRadius = 10; 25 | InsertSubview(visualEffectView, 0); 26 | } 27 | 28 | private CALayer CreateShadowLayer(CGRect rect) 29 | { 30 | var shadowLayer = new CALayer(); 31 | shadowLayer.BackgroundColor = UIColor.Black.CGColor; 32 | shadowLayer.ShadowOpacity = 1f; 33 | shadowLayer.ShadowRadius = 7; 34 | shadowLayer.ShadowOffset = new CGSize(0, 0); 35 | shadowLayer.ShadowColor = UIColor.Black.CGColor; 36 | shadowLayer.ShadowPath = UIBezierPath.FromRect(rect).CGPath; 37 | shadowLayer.MasksToBounds = false; 38 | return shadowLayer; 39 | } 40 | 41 | protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 42 | { 43 | base.OnElementPropertyChanged(sender, e); 44 | if (e.PropertyName=="BackgroundOpacity") 45 | { 46 | UpdateBackgroundColor(); 47 | } 48 | } 49 | 50 | private void UpdateBackgroundColor() 51 | { 52 | if (Element is DrawerControl dc) { 53 | var clr = dc.BackgroundColor.ToUIColor().ColorWithAlpha((float)dc.BackgroundOpacity); 54 | //var clr = UIColor.FromWhiteAlpha(1, (float)dc.BackgroundOpacity); 55 | Layer.BackgroundColor = clr.CGColor; 56 | } 57 | } 58 | 59 | protected override void OnElementChanged(ElementChangedEventArgs e) 60 | { 61 | base.OnElementChanged(e); 62 | Layer.MasksToBounds = true; 63 | Layer.CornerRadius = 10; 64 | //UpdateBackgroundColor(); 65 | } 66 | 67 | public override void Draw(CoreGraphics.CGRect rect) 68 | { 69 | 70 | visualEffectView.Frame = rect; 71 | Layer.InsertSublayer(CreateShadowLayer(new CGRect(rect.Width + 5, 6, 7, rect.Height + 5)), 0); 72 | Layer.InsertSublayer(CreateShadowLayer(new CGRect(6, rect.Height + 5, rect.Width, 7.0f)), 0); 73 | base.Draw(rect); 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.iOS/TinyControls.Drawer.iOS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {95B3D2D0-737F-4080-8563-825EFA7E27EF} 9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | TinyControls.Drawer 12 | TinyControls.Drawer 13 | Resources 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug 20 | DEBUG; 21 | prompt 22 | 4 23 | iPhone Developer 24 | true 25 | true 26 | true 27 | 13607 28 | false 29 | 30 | 31 | 32 | 33 | 34 | pdbonly 35 | true 36 | bin\Release 37 | 38 | prompt 39 | 4 40 | iPhone Developer 41 | true 42 | SdkOnly 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Core.dll 53 | 54 | 55 | ..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.dll 56 | 57 | 58 | ..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Platform.iOS.dll 59 | 60 | 61 | ..\packages\Xamarin.Forms.2.5.0.122203\lib\Xamarin.iOS10\Xamarin.Forms.Xaml.dll 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/TinyControls.Drawer.iOS/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/TinyTranslations.Forms/TinyTranslations.Forms.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.4 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/TinyTranslations.Forms/TranslateExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xamarin.Forms; 3 | using Xamarin.Forms.Xaml; 4 | 5 | namespace TinyTranslations.Forms 6 | { 7 | [ContentProperty("Text")] 8 | public class ansExtension : IMarkupExtension 9 | { 10 | public ansExtension() 11 | { 12 | } 13 | 14 | public static TranslationHelper Translator {get;set;} 15 | 16 | public string Text { get; set; } 17 | 18 | public string Key { get; set; } 19 | 20 | public object ProvideValue(IServiceProvider serviceProvider) 21 | { 22 | if (Text == null) 23 | return ""; 24 | 25 | if (Translator == null) 26 | return Text; 27 | 28 | if (string.IsNullOrEmpty(Key)) 29 | return Translator.Translations[Text]; 30 | else 31 | return Translator.Translate(Key, Text); 32 | 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/TinyTranslations.Forms/TranslationHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Threading.Tasks; 5 | using TinyTranslations; 6 | using Xamarin.Forms; 7 | 8 | namespace TinyTranslations.Forms 9 | { 10 | public class TranslationHelper : INotifyPropertyChanged 11 | { 12 | public TranslationHelper() 13 | { 14 | 15 | } 16 | 17 | public TranslationHelper(Uri serverUri) 18 | { 19 | httpClient = new TranslationClient(serverUri); 20 | SetFetchMethod(); 21 | } 22 | 23 | public TranslationHelper(TranslationClient client) 24 | { 25 | httpClient = client; 26 | SetFetchMethod(); 27 | } 28 | 29 | private void SetFetchMethod() { 30 | FetchLanguageMethod = (locale) => 31 | { 32 | return httpClient.GetTranslations(locale); 33 | }; 34 | } 35 | 36 | public static readonly string DeviceSpecificLanguage = 37 | System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName; 38 | 39 | public string Translate(string key, string text) 40 | { 41 | if (Translations.ContainsKey(key)) 42 | { 43 | return Translations[key]; 44 | } 45 | else 46 | { 47 | Translations.Add(key, text); 48 | return text; 49 | } 50 | } 51 | 52 | private TranslationClient httpClient; 53 | 54 | string currentLocale = DeviceSpecificLanguage; 55 | 56 | public string CurrentLocale 57 | { 58 | get 59 | { 60 | return currentLocale; 61 | } 62 | set 63 | { 64 | if (currentLocale != value) 65 | { 66 | changeCulture(value); 67 | propertyChanged(nameof(CurrentLocale)); 68 | } 69 | } 70 | } 71 | 72 | private TranslationDictionary _translations; 73 | 74 | public TranslationDictionary Translations 75 | { 76 | get 77 | { 78 | if (_translations == null) 79 | { 80 | _translations = new TranslationDictionary(currentLocale); 81 | changeCulture(currentLocale); 82 | } 83 | return _translations; 84 | } 85 | } 86 | 87 | public virtual Func> FetchLanguageMethod { get; set; } 88 | 89 | public bool IsActive => throw new NotImplementedException(); 90 | 91 | private void propertyChanged(string name) 92 | { 93 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); 94 | } 95 | 96 | private void changeCulture(string locale) 97 | { 98 | Task.Run(async () => await ChangeCultureAsync(locale)); 99 | } 100 | 101 | public async Task ChangeCultureAsync(string locale) 102 | { 103 | currentLocale = locale; 104 | var newdict = await FetchLanguageMethod(locale); 105 | newdict.OnAdd += TranslationAdded; 106 | if (_translations != null) 107 | _translations.OnAdd -= TranslationAdded; 108 | _translations = newdict; 109 | propertyChanged(nameof(Translations)); 110 | } 111 | 112 | void TranslationAdded(object sender, KeyValuePair e) => 113 | Task.Run(async () => 114 | { 115 | await httpClient.AddTranslationAsync(e); 116 | }); 117 | 118 | public void Init(string locale = "") 119 | { 120 | if (string.IsNullOrEmpty(locale)) 121 | locale = DeviceSpecificLanguage; 122 | Task.Run(async () => await ChangeCultureAsync(locale)); 123 | 124 | } 125 | 126 | public event PropertyChangedEventHandler PropertyChanged; 127 | 128 | public string Translate(string key) 129 | { 130 | return Translations[key]; 131 | } 132 | 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/TinyTranslatons/TinyTranslations.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.4 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/TinyTranslatons/TranslationClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using TinyTranslations; 6 | 7 | namespace TinyTranslations 8 | { 9 | public class TranslationClient 10 | { 11 | public TranslationClient(Uri baseUrl) 12 | { 13 | baseUri = baseUrl; 14 | } 15 | 16 | private Uri baseUri; 17 | 18 | private HttpClient _client; 19 | 20 | public HttpMessageHandler MessageHandler { get; set; } 21 | 22 | public virtual HttpClient GetHttpClient() 23 | { 24 | if (_client == null) 25 | { 26 | if (MessageHandler==null) { 27 | _client = new HttpClient(); 28 | } 29 | else { 30 | _client = new HttpClient(MessageHandler); 31 | } 32 | } 33 | return _client; 34 | } 35 | 36 | public virtual async Task GetTranslations(string locale) 37 | { 38 | var ret = new TranslationDictionary(locale); 39 | var translationString = await GetHttpClient().GetStringAsync(baseUri.AbsoluteUri + "api/translation/" + locale); 40 | var dict = Newtonsoft.Json.JsonConvert.DeserializeObject>(translationString); 41 | ret.Populate(locale, dict); 42 | return ret; 43 | } 44 | 45 | private string GetUrl(string value) { 46 | var ret = System.Net.WebUtility.UrlEncode(value); 47 | return ret.Replace("+","%20"); 48 | } 49 | 50 | public async Task AddTranslationAsync(KeyValuePair e) 51 | { 52 | var baseUrl = baseUri.AbsoluteUri + "api/translation/default/" + GetUrl(e.Key); 53 | if (e.Key.Equals(e.Value)) 54 | return await GetHttpClient().GetStringAsync(baseUrl); 55 | else 56 | { 57 | var ret = await GetHttpClient().PutAsync(baseUrl + "/" + GetUrl(e.Value), null); 58 | return e.Value; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/TinyTranslatons/TranslationDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Specialized; 5 | 6 | namespace TinyTranslations 7 | { 8 | public class NotTranslatedWord 9 | { 10 | public NotTranslatedWord(string key) 11 | { 12 | Original = key; 13 | UsageCount = 1; 14 | Translation = Original; 15 | } 16 | 17 | public string Original { get; set; } 18 | public string Translation { get; set; } 19 | public int UsageCount { get; set; } 20 | } 21 | 22 | public class TranslationDictionary : IDictionary, INotifyCollectionChanged 23 | { 24 | public TranslationDictionary(string locale) 25 | { 26 | Locale = locale; 27 | } 28 | 29 | private Dictionary dict = new Dictionary(); 30 | private Dictionary newWords = new Dictionary(); 31 | 32 | public static bool AddNewWordsToDictionary { get; set; } = true; 33 | 34 | public EventHandler> OnAdd; 35 | public EventHandler> OnUpdate; 36 | 37 | public string Locale { get; internal set; } 38 | 39 | public string this[string key] 40 | { 41 | get 42 | { 43 | if (dict.ContainsKey(key)) 44 | return dict[key]; 45 | return AddNotFound(key); 46 | } 47 | set 48 | { 49 | if (dict.ContainsKey(key)) 50 | { 51 | dict[key] = value; 52 | } 53 | Add(key, value); 54 | } 55 | } 56 | 57 | public void Populate(string locale, Dictionary data) 58 | { 59 | Locale = locale; 60 | foreach (var kv in data) 61 | { 62 | Add(kv); 63 | } 64 | } 65 | 66 | public Dictionary GetAllTranslations() => dict; 67 | 68 | private KeyValuePair GetKV(string key, string value = null) 69 | { 70 | return new KeyValuePair(key, value ?? dict[key]); 71 | } 72 | 73 | private void ChangeKeyValue(string key, string newValue) 74 | { 75 | //KeyValuePair old = KeyValuePair; 76 | var oldKv = GetKV(key); 77 | dict[key] = newValue; 78 | var newKv = GetKV(key, newValue); 79 | OnUpdate?.Invoke(this, newKv); 80 | CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, oldKv, newKv)); 81 | } 82 | 83 | private void AddValue(string key, string value) 84 | { 85 | if (dict.ContainsKey(key)) 86 | { 87 | ChangeKeyValue(key, value); 88 | } 89 | else 90 | { 91 | dict.Add(key, value); 92 | var newKv = GetKV(key, value); 93 | OnAdd?.Invoke(this, newKv); 94 | CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newKv)); 95 | } 96 | } 97 | 98 | private bool RemoveKey(string key) 99 | { 100 | if (dict.ContainsKey(key)) 101 | { 102 | CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, GetKV(key))); 103 | dict.Remove(key); 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | private string AddNotFound(string key) 110 | { 111 | if (newWords.ContainsKey(key)) 112 | { 113 | var word = newWords[key]; 114 | word.UsageCount++; 115 | return word.Translation; 116 | } 117 | var newword = new NotTranslatedWord(key); 118 | newWords.Add(key, newword); 119 | if (AddNewWordsToDictionary) 120 | { 121 | Add(key, newword.Translation); 122 | 123 | } 124 | return newword.Translation; 125 | 126 | } 127 | 128 | public ICollection Keys => dict.Keys; 129 | 130 | public ICollection Values => dict.Values; 131 | 132 | public int Count => dict.Count; 133 | 134 | public bool IsReadOnly => false; 135 | 136 | public bool IsPrimaryLanguage { get; set; } 137 | 138 | public event NotifyCollectionChangedEventHandler CollectionChanged; 139 | 140 | public void Add(string key, string value) 141 | { 142 | if (dict.ContainsKey(key)) 143 | { 144 | ChangeKeyValue(key, value); 145 | } 146 | else 147 | { 148 | AddValue(key, value); 149 | } 150 | } 151 | 152 | public void Add(KeyValuePair item) 153 | { 154 | Add(item.Key, item.Value); 155 | } 156 | 157 | public void Clear() 158 | { 159 | dict.Clear(); 160 | CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 161 | } 162 | 163 | public bool Contains(KeyValuePair item) => dict.ContainsKey(item.Key); 164 | 165 | public bool ContainsKey(string key) => dict.ContainsKey(key); 166 | 167 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 168 | { 169 | foreach (var kv in array) 170 | { 171 | Add(kv.Key, kv.Value); 172 | } 173 | } 174 | 175 | public IEnumerator> GetEnumerator() => dict.GetEnumerator(); 176 | 177 | public bool Remove(string key) 178 | { 179 | return RemoveKey(key); 180 | } 181 | 182 | public bool Remove(KeyValuePair item) 183 | { 184 | return Remove(item.Key); 185 | } 186 | 187 | public bool TryGetValue(string key, out string value) 188 | { 189 | return TryGetValue(key, out value); 190 | } 191 | 192 | IEnumerator IEnumerable.GetEnumerator() => dict.GetEnumerator(); 193 | 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/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 your 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/samples/Forms/Droid/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Android.App; 4 | using Android.Content; 5 | using Android.Content.PM; 6 | using Android.Runtime; 7 | using Android.Views; 8 | using Android.Widget; 9 | using Android.OS; 10 | using Plugin.Permissions; 11 | 12 | namespace gymlocator.Droid 13 | { 14 | [Activity(Label = "gymlocator.Droid", Icon = "@drawable/icon", Theme = "@style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] 15 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity 16 | { 17 | protected override void OnCreate(Bundle bundle) 18 | { 19 | TabLayoutResource = Resource.Layout.Tabbar; 20 | ToolbarResource = Resource.Layout.Toolbar; 21 | 22 | base.OnCreate(bundle); 23 | 24 | global::Xamarin.Forms.Forms.Init(this, bundle); 25 | global::Xamarin.FormsMaps.Init(this, bundle); 26 | 27 | //TKGoogleMaps.Init(this, bundle); 28 | LoadApplication(new App()); 29 | } 30 | 31 | public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) 32 | { 33 | PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/MainApplication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Android.App; 4 | using Android.OS; 5 | using Android.Runtime; 6 | using Plugin.CurrentActivity; 7 | 8 | namespace gymlocator.Droid 9 | { 10 | //You can specify additional application information in this attribute 11 | [Application] 12 | public class MainApplication : Application, Application.IActivityLifecycleCallbacks 13 | { 14 | public MainApplication(IntPtr handle, JniHandleOwnership transer) 15 | :base(handle, transer) 16 | { 17 | } 18 | 19 | public override void OnCreate() 20 | { 21 | base.OnCreate(); 22 | RegisterActivityLifecycleCallbacks(this); 23 | //A great place to initialize Xamarin.Insights and Dependency Services! 24 | } 25 | 26 | public override void OnTerminate() 27 | { 28 | base.OnTerminate(); 29 | UnregisterActivityLifecycleCallbacks(this); 30 | } 31 | 32 | public void OnActivityCreated(Activity activity, Bundle savedInstanceState) 33 | { 34 | CrossCurrentActivity.Current.Activity = activity; 35 | } 36 | 37 | public void OnActivityDestroyed(Activity activity) 38 | { 39 | } 40 | 41 | public void OnActivityPaused(Activity activity) 42 | { 43 | } 44 | 45 | public void OnActivityResumed(Activity activity) 46 | { 47 | CrossCurrentActivity.Current.Activity = activity; 48 | } 49 | 50 | public void OnActivitySaveInstanceState(Activity activity, Bundle outState) 51 | { 52 | } 53 | 54 | public void OnActivityStarted(Activity activity) 55 | { 56 | CrossCurrentActivity.Current.Activity = activity; 57 | } 58 | 59 | public void OnActivityStopped(Activity activity) 60 | { 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using Android.App; 4 | 5 | // Information about this assembly is defined by the following attributes. 6 | // Change them to the values specific to your project. 7 | 8 | [assembly: AssemblyTitle("gymlocator.Droid")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 18 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 19 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 20 | 21 | [assembly: AssemblyVersion("1.0.0")] 22 | 23 | // The following attributes are used to specify the signing key for the assembly, 24 | // if desired. See the Mono documentation for more information about signing. 25 | 26 | //[assembly: AssemblyDelaySign(false)] 27 | //[assembly: AssemblyKeyFile("")] 28 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/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.axml), 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/ 12 | icon.png 13 | 14 | layout/ 15 | main.axml 16 | 17 | values/ 18 | strings.xml 19 | 20 | In order to get the build system to recognize Android resources, set the build action to 21 | "AndroidResource". The native Android APIs do not operate directly with filenames, but 22 | instead operate on resource IDs. When you compile an Android application that uses resources, 23 | the build system will package the resources for distribution and generate a class called "R" 24 | (this is an Android convention) that contains the tokens for each one of the resources 25 | included. For example, for the above Resources layout, this is what the R class would expose: 26 | 27 | public class R { 28 | public class drawable { 29 | public const int icon = 0x123; 30 | } 31 | 32 | public class layout { 33 | public const int main = 0x456; 34 | } 35 | 36 | public class strings { 37 | public const int first_string = 0xabc; 38 | public const int second_string = 0xbcd; 39 | } 40 | } 41 | 42 | You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main 43 | to reference the layout/main.axml file, or R.strings.first_string to reference the first 44 | string in the dictionary file values/strings.xml. 45 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyStuff/TinyCache/acc14b2ee140af94dcbf0134ef494664df515b85/src/samples/Forms/Droid/Resources/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyStuff/TinyCache/acc14b2ee140af94dcbf0134ef494664df515b85/src/samples/Forms/Droid/Resources/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyStuff/TinyCache/acc14b2ee140af94dcbf0134ef494664df515b85/src/samples/Forms/Droid/Resources/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TinyStuff/TinyCache/acc14b2ee140af94dcbf0134ef494664df515b85/src/samples/Forms/Droid/Resources/drawable/icon.png -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/layout/Tabbar.axml: -------------------------------------------------------------------------------- 1 |  2 | 13 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/layout/Toolbar.axml: -------------------------------------------------------------------------------- 1 |  2 | 10 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 7 | 8 | 35 | 41 | 42 | -------------------------------------------------------------------------------- /src/samples/Forms/Droid/packages.config: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/CacheResources/reload.json: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/DataStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using gymlocator.Core.Shopping; 5 | using gymlocator.Core.Shopping.Models; 6 | using gymlocator.Rest; 7 | using gymlocator.Rest.Models; 8 | using Microsoft.Rest; 9 | using TinyCacheLib; 10 | 11 | namespace gymlocator.Core 12 | { 13 | public class NoClientCredentials : ServiceClientCredentials 14 | { 15 | 16 | } 17 | 18 | public class ShoppingService 19 | { 20 | private IShoppingAPI _client; 21 | 22 | public ShoppingService() 23 | { 24 | var retryHandler = new TinyRetryDelegationHandler(); 25 | retryHandler.RetryOn(); 26 | //_client = new ShoppingAPI(new Uri("http://localhost:5000"), new UnsafeCredentials(), new TinyCache.TinyCacheDelegationHandler()); 27 | _client = new ShoppingAPI(new Uri("http://localhost:5000"), new NoClientCredentials(), retryHandler); 28 | } 29 | 30 | public async Task> GetShoppingLists() 31 | { 32 | var data = await TinyCache.RunAsync>("shoppingLists10", async () => { 33 | var ret = await _client.GetShoppingListsAsync(); 34 | return ret; 35 | }); 36 | return data; 37 | } 38 | 39 | public async Task AddItem(Item item) 40 | { 41 | await _client.AddListItemAsync(item); 42 | } 43 | 44 | public async Task AddList(ShoppingList item) 45 | { 46 | await _client.AddShoppingListAsync(item); 47 | } 48 | 49 | public async Task> GetListItems(int listId) 50 | { 51 | var data = await TinyCache.RunAsync("listItems" + listId, async () => { 52 | var ret = await _client.GetListItemsAsync(listId); 53 | return ret; 54 | }); 55 | return data; 56 | } 57 | 58 | public async Task UpdateList(ShoppingList shoppingList) 59 | { 60 | await _client.UpdateShoppingListAsync(shoppingList.Id, shoppingList); 61 | } 62 | } 63 | 64 | public class DataStore 65 | { 66 | static Uri apiEndPoint = new Uri("http://f24s-gym-api034.azurewebsites.net/v13/"); 67 | static TinyRetryDelegationHandler retryHandler = new TinyRetryDelegationHandler(); 68 | GymAPI api = new GymAPI(apiEndPoint, new NoClientCredentials(), retryHandler); 69 | private string locale = System.Globalization.CultureInfo.CurrentCulture.Name; 70 | 71 | XamarinPropertyStorage store = new XamarinPropertyStorage(); 72 | 73 | public DataStore() 74 | { 75 | 76 | retryHandler.RetryOn(); 77 | 78 | 79 | api.BaseUri = apiEndPoint; 80 | store.LoadFromString(CacheResources.PreloadData.JsonData); 81 | 82 | var preloadString = store.GetAllAsLoadableString(); 83 | 84 | TinyCache.SetCacheStore(store); 85 | TinyCache.OnError += (sender, e) => 86 | { 87 | var i = 3; 88 | }; 89 | 90 | TinyCache.SetBasePolicy( 91 | new TinyCachePolicy() 92 | .SetMode(TinyCacheModeEnum.CacheFirst) 93 | .SetFetchTimeout(TimeSpan.FromSeconds(5)) // 5 second excecution limit 94 | .SetExpirationTime(TimeSpan.FromMinutes(10)) // 10 minute expiration before next fetch 95 | .SetUpdateCacheTimeout(50) // Wait 50ms before trying to update cache in background 96 | ); 97 | } 98 | 99 | public async Task> GetGymsAsync() 100 | { 101 | //var result = await api.GetGymsAsync(locale) as IList; 102 | var result = await TinyCache.RunAsync("gyms", () => { return api.GetGymListAsync(locale); }); 103 | return result; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/GymAPIExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest 8 | { 9 | using Models; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | /// 14 | /// Extension methods for GymAPI. 15 | /// 16 | public static partial class GymAPIExtensions 17 | { 18 | /// 19 | /// Get all the gyms for the specified country and language. 20 | /// 21 | /// 22 | /// Will get an array of gyms for the specific country in the specified 23 | /// language. If no gyms are found, the result will be an empty array. 24 | /// 25 | /// 26 | /// The operations group for this extension method. 27 | /// 28 | /// 29 | /// Language and country specifier for the gyms you want to retrieve. The 30 | /// format is `{lang}-{country}`, example `/gyms/sv-se`. Note that the locale 31 | /// is case sensitive. 32 | /// 33 | public static object GetGyms(this IGymAPI operations, string locale) 34 | { 35 | return operations.GetGymsAsync(locale).GetAwaiter().GetResult(); 36 | } 37 | 38 | /// 39 | /// Get all the gyms for the specified country and language. 40 | /// 41 | /// 42 | /// Will get an array of gyms for the specific country in the specified 43 | /// language. If no gyms are found, the result will be an empty array. 44 | /// 45 | /// 46 | /// The operations group for this extension method. 47 | /// 48 | /// 49 | /// Language and country specifier for the gyms you want to retrieve. The 50 | /// format is `{lang}-{country}`, example `/gyms/sv-se`. Note that the locale 51 | /// is case sensitive. 52 | /// 53 | /// 54 | /// The cancellation token. 55 | /// 56 | public static async Task GetGymsAsync(this IGymAPI operations, string locale, CancellationToken cancellationToken = default(CancellationToken)) 57 | { 58 | using (var _result = await operations.GetGymsWithHttpMessagesAsync(locale, null, cancellationToken).ConfigureAwait(false)) 59 | { 60 | return _result.Body; 61 | } 62 | } 63 | 64 | /// 65 | /// Get a specific gym by locale and ID. 66 | /// 67 | /// 68 | /// Will get a specific gym by ID and country/language. If the gym is not found 69 | /// it will return status 404. 70 | /// 71 | /// 72 | /// The operations group for this extension method. 73 | /// 74 | /// 75 | /// Language and country specifier for the gyms you want to retrieve. The 76 | /// format is `{lang}-{country}`, example `/gyms/sv-se`. Note that the locale 77 | /// is case sensitive. 78 | /// 79 | /// 80 | /// Unique identifier for the gym you want to retrieve. 81 | /// 82 | public static object GetGym(this IGymAPI operations, string locale, string gymID) 83 | { 84 | return operations.GetGymAsync(locale, gymID).GetAwaiter().GetResult(); 85 | } 86 | 87 | /// 88 | /// Get a specific gym by locale and ID. 89 | /// 90 | /// 91 | /// Will get a specific gym by ID and country/language. If the gym is not found 92 | /// it will return status 404. 93 | /// 94 | /// 95 | /// The operations group for this extension method. 96 | /// 97 | /// 98 | /// Language and country specifier for the gyms you want to retrieve. The 99 | /// format is `{lang}-{country}`, example `/gyms/sv-se`. Note that the locale 100 | /// is case sensitive. 101 | /// 102 | /// 103 | /// Unique identifier for the gym you want to retrieve. 104 | /// 105 | /// 106 | /// The cancellation token. 107 | /// 108 | public static async Task GetGymAsync(this IGymAPI operations, string locale, string gymID, CancellationToken cancellationToken = default(CancellationToken)) 109 | { 110 | using (var _result = await operations.GetGymWithHttpMessagesAsync(locale, gymID, null, cancellationToken).ConfigureAwait(false)) 111 | { 112 | return _result.Body; 113 | } 114 | } 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/IGymAPI.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest 8 | { 9 | using Microsoft.Rest; 10 | using Models; 11 | using Newtonsoft.Json; 12 | using System.Collections; 13 | using System.Collections.Generic; 14 | using System.Threading; 15 | using System.Threading.Tasks; 16 | 17 | /// 18 | /// The Gym API is responsible for the Gym entity and its cross cut 19 | /// concerns. All requests for gym information should go through this API. 20 | /// All responses are in JSON format, and camel cased with leading 21 | /// lowercase. 22 | /// No properties are skipped. If a property is not set in the source, it 23 | /// will be set to null. List properties will not be null, instead they 24 | /// will be empty array if no value exist in source. Default value of 25 | /// numbers will be null, and default values of booleans will be false. 26 | /// Locale is required in every request. This is a case sensitive 27 | /// combination of language code and country code. 28 | /// 29 | public partial interface IGymAPI : System.IDisposable 30 | { 31 | /// 32 | /// The base URI of the service. 33 | /// 34 | System.Uri BaseUri { get; set; } 35 | 36 | /// 37 | /// Gets or sets json serialization settings. 38 | /// 39 | JsonSerializerSettings SerializationSettings { get; } 40 | 41 | /// 42 | /// Gets or sets json deserialization settings. 43 | /// 44 | JsonSerializerSettings DeserializationSettings { get; } 45 | 46 | /// 47 | /// Subscription credentials which uniquely identify client 48 | /// subscription. 49 | /// 50 | ServiceClientCredentials Credentials { get; } 51 | 52 | 53 | /// 54 | /// Get all the gyms for the specified country and language. 55 | /// 56 | /// 57 | /// Will get an array of gyms for the specific country in the specified 58 | /// language. If no gyms are found, the result will be an empty array. 59 | /// 60 | /// 61 | /// Language and country specifier for the gyms you want to retrieve. 62 | /// The format is `{lang}-{country}`, example `/gyms/sv-se`. Note that 63 | /// the locale is case sensitive. 64 | /// 65 | /// 66 | /// The headers that will be added to request. 67 | /// 68 | /// 69 | /// The cancellation token. 70 | /// 71 | Task> GetGymsWithHttpMessagesAsync(string locale, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); 72 | 73 | /// 74 | /// Get a specific gym by locale and ID. 75 | /// 76 | /// 77 | /// Will get a specific gym by ID and country/language. If the gym is 78 | /// not found it will return status 404. 79 | /// 80 | /// 81 | /// Language and country specifier for the gyms you want to retrieve. 82 | /// The format is `{lang}-{country}`, example `/gyms/sv-se`. Note that 83 | /// the locale is case sensitive. 84 | /// 85 | /// 86 | /// Unique identifier for the gym you want to retrieve. 87 | /// 88 | /// 89 | /// The headers that will be added to request. 90 | /// 91 | /// 92 | /// The cancellation token. 93 | /// 94 | Task> GetGymWithHttpMessagesAsync(string locale, string gymID, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/Address.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Linq; 12 | 13 | /// 14 | /// Address to the gym. 15 | /// 16 | public partial class Address 17 | { 18 | /// 19 | /// Initializes a new instance of the Address class. 20 | /// 21 | public Address() 22 | { 23 | CustomInit(); 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the Address class. 28 | /// 29 | /// Street name and number. 30 | /// The city address. 31 | /// The postal address code. 32 | /// Refers to a business unit in Google 33 | /// Maps 34 | public Address(string streetAddress, string city, string postalCode = default(string), string googlePlaceID = default(string)) 35 | { 36 | StreetAddress = streetAddress; 37 | PostalCode = postalCode; 38 | City = city; 39 | GooglePlaceID = googlePlaceID; 40 | CustomInit(); 41 | } 42 | 43 | /// 44 | /// An initialization method that performs custom operations like setting defaults 45 | /// 46 | partial void CustomInit(); 47 | 48 | /// 49 | /// Gets or sets street name and number. 50 | /// 51 | [JsonProperty(PropertyName = "streetAddress")] 52 | public string StreetAddress { get; set; } 53 | 54 | /// 55 | /// Gets or sets the postal address code. 56 | /// 57 | [JsonProperty(PropertyName = "postalCode")] 58 | public string PostalCode { get; set; } 59 | 60 | /// 61 | /// Gets or sets the city address. 62 | /// 63 | [JsonProperty(PropertyName = "city")] 64 | public string City { get; set; } 65 | 66 | /// 67 | /// Gets or sets refers to a business unit in Google Maps 68 | /// 69 | [JsonProperty(PropertyName = "googlePlaceID")] 70 | public string GooglePlaceID { get; set; } 71 | 72 | /// 73 | /// Validate the object. 74 | /// 75 | /// 76 | /// Thrown if validation fails 77 | /// 78 | public virtual void Validate() 79 | { 80 | if (StreetAddress == null) 81 | { 82 | throw new ValidationException(ValidationRules.CannotBeNull, "StreetAddress"); 83 | } 84 | if (City == null) 85 | { 86 | throw new ValidationException(ValidationRules.CannotBeNull, "City"); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/ContactDetails.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Newtonsoft.Json; 10 | using System.Linq; 11 | 12 | /// 13 | /// Details for getting in touch with the gym. 14 | /// 15 | public partial class ContactDetails 16 | { 17 | /// 18 | /// Initializes a new instance of the ContactDetails class. 19 | /// 20 | public ContactDetails() 21 | { 22 | CustomInit(); 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the ContactDetails class. 27 | /// 28 | /// E-mail address to the gym 29 | /// Phone number to the gym. Could be an 30 | /// international phone number. 31 | public ContactDetails(string email = default(string), string phone = default(string)) 32 | { 33 | Email = email; 34 | Phone = phone; 35 | CustomInit(); 36 | } 37 | 38 | /// 39 | /// An initialization method that performs custom operations like setting defaults 40 | /// 41 | partial void CustomInit(); 42 | 43 | /// 44 | /// Gets or sets e-mail address to the gym 45 | /// 46 | [JsonProperty(PropertyName = "email")] 47 | public string Email { get; set; } 48 | 49 | /// 50 | /// Gets or sets phone number to the gym. Could be an international 51 | /// phone number. 52 | /// 53 | [JsonProperty(PropertyName = "phone")] 54 | public string Phone { get; set; } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/Coordinates.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Newtonsoft.Json; 10 | using System.Linq; 11 | 12 | /// 13 | /// Longitude and latitude coordinates for a position of a gym. 14 | /// 15 | public partial class Coordinates 16 | { 17 | /// 18 | /// Initializes a new instance of the Coordinates class. 19 | /// 20 | public Coordinates() 21 | { 22 | CustomInit(); 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the Coordinates class. 27 | /// 28 | /// Longitude 29 | /// Latitude 30 | public Coordinates(double lng, double lat) 31 | { 32 | Lng = lng; 33 | Lat = lat; 34 | CustomInit(); 35 | } 36 | 37 | /// 38 | /// An initialization method that performs custom operations like setting defaults 39 | /// 40 | partial void CustomInit(); 41 | 42 | /// 43 | /// Gets or sets longitude 44 | /// 45 | [JsonProperty(PropertyName = "lng")] 46 | public double Lng { get; set; } 47 | 48 | /// 49 | /// Gets or sets latitude 50 | /// 51 | [JsonProperty(PropertyName = "lat")] 52 | public double Lat { get; set; } 53 | 54 | /// 55 | /// Validate the object. 56 | /// 57 | /// 58 | /// Thrown if validation fails 59 | /// 60 | public virtual void Validate() 61 | { 62 | //Nothing to validate 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/Error.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Linq; 12 | 13 | public partial class Error 14 | { 15 | /// 16 | /// Initializes a new instance of the Error class. 17 | /// 18 | public Error() 19 | { 20 | CustomInit(); 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the Error class. 25 | /// 26 | /// Unique number that identifies this type of 27 | /// error. 28 | /// Human friendly error message. 29 | public Error(int number, string message) 30 | { 31 | Number = number; 32 | Message = message; 33 | CustomInit(); 34 | } 35 | 36 | /// 37 | /// An initialization method that performs custom operations like setting defaults 38 | /// 39 | partial void CustomInit(); 40 | 41 | /// 42 | /// Gets or sets unique number that identifies this type of error. 43 | /// 44 | [JsonProperty(PropertyName = "number")] 45 | public int Number { get; set; } 46 | 47 | /// 48 | /// Gets or sets human friendly error message. 49 | /// 50 | [JsonProperty(PropertyName = "message")] 51 | public string Message { get; set; } 52 | 53 | /// 54 | /// Validate the object. 55 | /// 56 | /// 57 | /// Thrown if validation fails 58 | /// 59 | public virtual void Validate() 60 | { 61 | if (Message == null) 62 | { 63 | throw new ValidationException(ValidationRules.CannotBeNull, "Message"); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/Feature.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Linq; 12 | 13 | /// 14 | /// A feature is some activity or type of workout available on a gym. 15 | /// 16 | public partial class Feature 17 | { 18 | /// 19 | /// Initializes a new instance of the Feature class. 20 | /// 21 | public Feature() 22 | { 23 | CustomInit(); 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the Feature class. 28 | /// 29 | /// Unique identifier for this feature 30 | /// The name of this feature 31 | /// A sentence about what this feature 32 | /// is. 33 | public Feature(string id, string name, string description = default(string)) 34 | { 35 | Id = id; 36 | Name = name; 37 | Description = description; 38 | CustomInit(); 39 | } 40 | 41 | /// 42 | /// An initialization method that performs custom operations like setting defaults 43 | /// 44 | partial void CustomInit(); 45 | 46 | /// 47 | /// Gets or sets unique identifier for this feature 48 | /// 49 | [JsonProperty(PropertyName = "id")] 50 | public string Id { get; set; } 51 | 52 | /// 53 | /// Gets or sets the name of this feature 54 | /// 55 | [JsonProperty(PropertyName = "name")] 56 | public string Name { get; set; } 57 | 58 | /// 59 | /// Gets or sets a sentence about what this feature is. 60 | /// 61 | [JsonProperty(PropertyName = "description")] 62 | public string Description { get; set; } 63 | 64 | /// 65 | /// Validate the object. 66 | /// 67 | /// 68 | /// Thrown if validation fails 69 | /// 70 | public virtual void Validate() 71 | { 72 | if (Id == null) 73 | { 74 | throw new ValidationException(ValidationRules.CannotBeNull, "Id"); 75 | } 76 | if (Name == null) 77 | { 78 | throw new ValidationException(ValidationRules.CannotBeNull, "Name"); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/GroupTrainingType.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | 15 | /// 16 | /// A kind of group training. 17 | /// 18 | public partial class GroupTrainingType 19 | { 20 | /// 21 | /// Initializes a new instance of the GroupTrainingType class. 22 | /// 23 | public GroupTrainingType() 24 | { 25 | CustomInit(); 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the GroupTrainingType class. 30 | /// 31 | /// A unique lowercase string identifier that will not 32 | /// change. 33 | /// Human friendly name of this type of 34 | /// workout. 35 | /// A description that will tell a reader 36 | /// what this group training type is. 37 | /// An array of features that this group 38 | /// training type is included in 39 | public GroupTrainingType(string id, string name = default(string), Image desktopImage = default(Image), Image mobileImage = default(Image), string description = default(string), IList features = default(IList)) 40 | { 41 | Id = id; 42 | Name = name; 43 | DesktopImage = desktopImage; 44 | MobileImage = mobileImage; 45 | Description = description; 46 | Features = features; 47 | CustomInit(); 48 | } 49 | 50 | /// 51 | /// An initialization method that performs custom operations like setting defaults 52 | /// 53 | partial void CustomInit(); 54 | 55 | /// 56 | /// Gets or sets a unique lowercase string identifier that will not 57 | /// change. 58 | /// 59 | [JsonProperty(PropertyName = "id")] 60 | public string Id { get; set; } 61 | 62 | /// 63 | /// Gets or sets human friendly name of this type of workout. 64 | /// 65 | [JsonProperty(PropertyName = "name")] 66 | public string Name { get; set; } 67 | 68 | /// 69 | /// 70 | [JsonProperty(PropertyName = "desktopImage")] 71 | public Image DesktopImage { get; set; } 72 | 73 | /// 74 | /// 75 | [JsonProperty(PropertyName = "mobileImage")] 76 | public Image MobileImage { get; set; } 77 | 78 | /// 79 | /// Gets or sets a description that will tell a reader what this group 80 | /// training type is. 81 | /// 82 | [JsonProperty(PropertyName = "description")] 83 | public string Description { get; set; } 84 | 85 | /// 86 | /// Gets or sets an array of features that this group training type is 87 | /// included in 88 | /// 89 | [JsonProperty(PropertyName = "features")] 90 | public IList Features { get; set; } 91 | 92 | /// 93 | /// Validate the object. 94 | /// 95 | /// 96 | /// Thrown if validation fails 97 | /// 98 | public virtual void Validate() 99 | { 100 | if (Id == null) 101 | { 102 | throw new ValidationException(ValidationRules.CannotBeNull, "Id"); 103 | } 104 | if (DesktopImage != null) 105 | { 106 | DesktopImage.Validate(); 107 | } 108 | if (MobileImage != null) 109 | { 110 | MobileImage.Validate(); 111 | } 112 | if (Features != null) 113 | { 114 | foreach (var element in Features) 115 | { 116 | if (element != null) 117 | { 118 | element.Validate(); 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/Image.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Linq; 12 | 13 | /// 14 | /// Represents an image with meta data. 15 | /// 16 | public partial class Image 17 | { 18 | /// 19 | /// Initializes a new instance of the Image class. 20 | /// 21 | public Image() 22 | { 23 | CustomInit(); 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the Image class. 28 | /// 29 | /// Name of the image, filename possibly 30 | /// Full URL to the image location 31 | /// Width of the image in pixels. 32 | /// Height of the image in pixels. 33 | /// Description of the image, possible use as 34 | /// alt tag 35 | public Image(string name, string url, int width, int height, string description = default(string)) 36 | { 37 | Name = name; 38 | Description = description; 39 | Url = url; 40 | Width = width; 41 | Height = height; 42 | CustomInit(); 43 | } 44 | 45 | /// 46 | /// An initialization method that performs custom operations like setting defaults 47 | /// 48 | partial void CustomInit(); 49 | 50 | /// 51 | /// Gets or sets name of the image, filename possibly 52 | /// 53 | [JsonProperty(PropertyName = "name")] 54 | public string Name { get; set; } 55 | 56 | /// 57 | /// Gets or sets description of the image, possible use as alt tag 58 | /// 59 | [JsonProperty(PropertyName = "description")] 60 | public string Description { get; set; } 61 | 62 | /// 63 | /// Gets or sets full URL to the image location 64 | /// 65 | [JsonProperty(PropertyName = "url")] 66 | public string Url { get; set; } 67 | 68 | /// 69 | /// Gets or sets width of the image in pixels. 70 | /// 71 | [JsonProperty(PropertyName = "width")] 72 | public int Width { get; set; } 73 | 74 | /// 75 | /// Gets or sets height of the image in pixels. 76 | /// 77 | [JsonProperty(PropertyName = "height")] 78 | public int Height { get; set; } 79 | 80 | /// 81 | /// Validate the object. 82 | /// 83 | /// 84 | /// Thrown if validation fails 85 | /// 86 | public virtual void Validate() 87 | { 88 | if (Name == null) 89 | { 90 | throw new ValidationException(ValidationRules.CannotBeNull, "Name"); 91 | } 92 | if (Url == null) 93 | { 94 | throw new ValidationException(ValidationRules.CannotBeNull, "Url"); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/PersonalTrainer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Collections; 12 | using System.Collections.Generic; 13 | using System.Linq; 14 | 15 | /// 16 | /// A personal trainer for the gym. 17 | /// 18 | public partial class PersonalTrainer 19 | { 20 | /// 21 | /// Initializes a new instance of the PersonalTrainer class. 22 | /// 23 | public PersonalTrainer() 24 | { 25 | CustomInit(); 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the PersonalTrainer class. 30 | /// 31 | /// Full name of the personal trainer. 32 | /// What kind of workouts are this personal 33 | /// trainer's speciality. Empty array if none. 34 | /// Education of the personal trainer. Empty 35 | /// array if none. 36 | /// E-mail to the personal trainer. 37 | /// Phone number to the personal trainer. 38 | /// Presentation of the personal 39 | /// trainer 40 | public PersonalTrainer(string name, IList specialities, IList education, string email = default(string), string phone = default(string), string presentation = default(string), Image desktopImage = default(Image), Image mobileImage = default(Image)) 41 | { 42 | Name = name; 43 | Email = email; 44 | Phone = phone; 45 | Presentation = presentation; 46 | Specialities = specialities; 47 | Education = education; 48 | DesktopImage = desktopImage; 49 | MobileImage = mobileImage; 50 | CustomInit(); 51 | } 52 | 53 | /// 54 | /// An initialization method that performs custom operations like setting defaults 55 | /// 56 | partial void CustomInit(); 57 | 58 | /// 59 | /// Gets or sets full name of the personal trainer. 60 | /// 61 | [JsonProperty(PropertyName = "name")] 62 | public string Name { get; set; } 63 | 64 | /// 65 | /// Gets or sets e-mail to the personal trainer. 66 | /// 67 | [JsonProperty(PropertyName = "email")] 68 | public string Email { get; set; } 69 | 70 | /// 71 | /// Gets or sets phone number to the personal trainer. 72 | /// 73 | [JsonProperty(PropertyName = "phone")] 74 | public string Phone { get; set; } 75 | 76 | /// 77 | /// Gets or sets presentation of the personal trainer 78 | /// 79 | [JsonProperty(PropertyName = "presentation")] 80 | public string Presentation { get; set; } 81 | 82 | /// 83 | /// Gets or sets what kind of workouts are this personal trainer's 84 | /// speciality. Empty array if none. 85 | /// 86 | [JsonProperty(PropertyName = "specialities")] 87 | public IList Specialities { get; set; } 88 | 89 | /// 90 | /// Gets or sets education of the personal trainer. Empty array if 91 | /// none. 92 | /// 93 | [JsonProperty(PropertyName = "education")] 94 | public IList Education { get; set; } 95 | 96 | /// 97 | /// 98 | [JsonProperty(PropertyName = "desktopImage")] 99 | public Image DesktopImage { get; set; } 100 | 101 | /// 102 | /// 103 | [JsonProperty(PropertyName = "mobileImage")] 104 | public Image MobileImage { get; set; } 105 | 106 | /// 107 | /// Validate the object. 108 | /// 109 | /// 110 | /// Thrown if validation fails 111 | /// 112 | public virtual void Validate() 113 | { 114 | if (Name == null) 115 | { 116 | throw new ValidationException(ValidationRules.CannotBeNull, "Name"); 117 | } 118 | if (Specialities == null) 119 | { 120 | throw new ValidationException(ValidationRules.CannotBeNull, "Specialities"); 121 | } 122 | if (Education == null) 123 | { 124 | throw new ValidationException(ValidationRules.CannotBeNull, "Education"); 125 | } 126 | if (DesktopImage != null) 127 | { 128 | DesktopImage.Validate(); 129 | } 130 | if (MobileImage != null) 131 | { 132 | MobileImage.Validate(); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/SOSChildrensVillage.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Newtonsoft.Json; 10 | using System.Linq; 11 | 12 | /// 13 | /// The gym is sponsor of an SOS Children's Village project. 14 | /// 15 | public partial class SOSChildrensVillage 16 | { 17 | /// 18 | /// Initializes a new instance of the SOSChildrensVillage class. 19 | /// 20 | public SOSChildrensVillage() 21 | { 22 | CustomInit(); 23 | } 24 | 25 | /// 26 | /// Initializes a new instance of the SOSChildrensVillage class. 27 | /// 28 | /// The name of the SOS Barnby that this gym is 29 | /// sponsor of. 30 | public SOSChildrensVillage(string name = default(string), Image desktopImage = default(Image), Image mobileImage = default(Image)) 31 | { 32 | Name = name; 33 | DesktopImage = desktopImage; 34 | MobileImage = mobileImage; 35 | CustomInit(); 36 | } 37 | 38 | /// 39 | /// An initialization method that performs custom operations like setting defaults 40 | /// 41 | partial void CustomInit(); 42 | 43 | /// 44 | /// Gets or sets the name of the SOS Barnby that this gym is sponsor 45 | /// of. 46 | /// 47 | [JsonProperty(PropertyName = "name")] 48 | public string Name { get; set; } 49 | 50 | /// 51 | /// 52 | [JsonProperty(PropertyName = "desktopImage")] 53 | public Image DesktopImage { get; set; } 54 | 55 | /// 56 | /// 57 | [JsonProperty(PropertyName = "mobileImage")] 58 | public Image MobileImage { get; set; } 59 | 60 | /// 61 | /// Validate the object. 62 | /// 63 | /// 64 | /// Thrown if validation fails 65 | /// 66 | public virtual void Validate() 67 | { 68 | if (DesktopImage != null) 69 | { 70 | DesktopImage.Validate(); 71 | } 72 | if (MobileImage != null) 73 | { 74 | MobileImage.Validate(); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/Rest/Models/StaffedHour.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | using Newtonsoft.Json; 10 | using System.Linq; 11 | 12 | /// 13 | /// A time span with an optional label. From and To should always be the 14 | /// same date. Unless it is a deviation, only the weekday part of the date 15 | /// is of interest. If this is an exception date, the actual date is also 16 | /// interesting. 17 | /// 18 | public partial class StaffedHour 19 | { 20 | /// 21 | /// Initializes a new instance of the StaffedHour class. 22 | /// 23 | public StaffedHour() 24 | { 25 | CustomInit(); 26 | } 27 | 28 | /// 29 | /// Initializes a new instance of the StaffedHour class. 30 | /// 31 | /// Start time/date of staffed hour. The 32 | /// format is ISO-8601. 33 | /// End time/date of staffed hour. Date will always be 34 | /// the same date as `from`. The format is ISO-8601. 35 | /// This is a string for a deviation to the 36 | /// regular schedule. If this value is null, the record is part of the 37 | /// standard schedule. 38 | public StaffedHour(System.DateTime fromProperty, System.DateTime to, string deviation = default(string)) 39 | { 40 | FromProperty = fromProperty; 41 | To = to; 42 | Deviation = deviation; 43 | CustomInit(); 44 | } 45 | 46 | /// 47 | /// An initialization method that performs custom operations like setting defaults 48 | /// 49 | partial void CustomInit(); 50 | 51 | /// 52 | /// Gets or sets start time/date of staffed hour. The format is 53 | /// ISO-8601. 54 | /// 55 | [JsonProperty(PropertyName = "from")] 56 | public System.DateTime FromProperty { get; set; } 57 | 58 | /// 59 | /// Gets or sets end time/date of staffed hour. Date will always be the 60 | /// same date as `from`. The format is ISO-8601. 61 | /// 62 | [JsonProperty(PropertyName = "to")] 63 | public System.DateTime To { get; set; } 64 | 65 | /// 66 | /// Gets or sets this is a string for a deviation to the regular 67 | /// schedule. If this value is null, the record is part of the standard 68 | /// schedule. 69 | /// 70 | [JsonProperty(PropertyName = "deviation")] 71 | public string Deviation { get; set; } 72 | 73 | /// 74 | /// Validate the object. 75 | /// 76 | /// 77 | /// Thrown if validation fails 78 | /// 79 | public virtual void Validate() 80 | { 81 | //Nothing to validate 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/RestAddons/Gym.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace gymlocator.Rest.Models 8 | { 9 | [PropertyChanged.AddINotifyPropertyChangedInterface] 10 | public partial class Gym 11 | { 12 | public string Email 13 | { 14 | get 15 | { 16 | return Contact.Email; 17 | } 18 | } 19 | 20 | public int NoPT 21 | { 22 | get 23 | { 24 | return PersonalTrainers.Count(); 25 | } 26 | } 27 | 28 | public bool HasPT 29 | { 30 | get 31 | { 32 | return NoPT > 0; 33 | } 34 | } 35 | } 36 | 37 | public static class GymExtensions 38 | { 39 | public static async Task> GetGymListAsync(this IGymAPI operations, string locale, CancellationToken cancellationToken = default(CancellationToken)) 40 | { 41 | using (var _result = await operations.GetGymsWithHttpMessagesAsync(locale, null, cancellationToken).ConfigureAwait(false)) 42 | { 43 | return (_result.Body as IEnumerable).OrderBy(d => d.Name).ToList(); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/ShoppingNet/Models/Item.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Core.Shopping.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Linq; 12 | 13 | public partial class Item 14 | { 15 | /// 16 | /// Initializes a new instance of the Item class. 17 | /// 18 | public Item() 19 | { 20 | CustomInit(); 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the Item class. 25 | /// 26 | public Item(int id, System.DateTime added, System.DateTime done, string name, bool completed, int storeID, int listId, double lat, double lng, bool priceExists, double price, string barcode) 27 | { 28 | Id = id; 29 | Added = added; 30 | Done = done; 31 | Name = name; 32 | Completed = completed; 33 | StoreID = storeID; 34 | ListId = listId; 35 | Lat = lat; 36 | Lng = lng; 37 | PriceExists = priceExists; 38 | Price = price; 39 | Barcode = barcode; 40 | CustomInit(); 41 | } 42 | 43 | /// 44 | /// An initialization method that performs custom operations like setting defaults 45 | /// 46 | partial void CustomInit(); 47 | 48 | /// 49 | /// 50 | [JsonProperty(PropertyName = "id")] 51 | public int Id { get; set; } 52 | 53 | /// 54 | /// 55 | [JsonProperty(PropertyName = "added")] 56 | public System.DateTime Added { get; set; } 57 | 58 | /// 59 | /// 60 | [JsonProperty(PropertyName = "done")] 61 | public System.DateTime Done { get; set; } 62 | 63 | /// 64 | /// 65 | [JsonProperty(PropertyName = "name")] 66 | public string Name { get; set; } 67 | 68 | /// 69 | /// 70 | [JsonProperty(PropertyName = "completed")] 71 | public bool Completed { get; set; } 72 | 73 | /// 74 | /// 75 | [JsonProperty(PropertyName = "storeID")] 76 | public int StoreID { get; set; } 77 | 78 | /// 79 | /// 80 | [JsonProperty(PropertyName = "listId")] 81 | public int ListId { get; set; } 82 | 83 | /// 84 | /// 85 | [JsonProperty(PropertyName = "lat")] 86 | public double Lat { get; set; } 87 | 88 | /// 89 | /// 90 | [JsonProperty(PropertyName = "lng")] 91 | public double Lng { get; set; } 92 | 93 | /// 94 | /// 95 | [JsonProperty(PropertyName = "priceExists")] 96 | public bool PriceExists { get; set; } 97 | 98 | /// 99 | /// 100 | [JsonProperty(PropertyName = "price")] 101 | public double Price { get; set; } 102 | 103 | /// 104 | /// 105 | [JsonProperty(PropertyName = "barcode")] 106 | public string Barcode { get; set; } 107 | 108 | /// 109 | /// Validate the object. 110 | /// 111 | /// 112 | /// Thrown if validation fails 113 | /// 114 | public virtual void Validate() 115 | { 116 | if (Name == null) 117 | { 118 | throw new ValidationException(ValidationRules.CannotBeNull, "Name"); 119 | } 120 | if (Barcode == null) 121 | { 122 | throw new ValidationException(ValidationRules.CannotBeNull, "Barcode"); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/ShoppingNet/Models/ShoppingList.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Code generated by Microsoft (R) AutoRest Code Generator. 3 | // Changes may cause incorrect behavior and will be lost if the code is 4 | // regenerated. 5 | // 6 | 7 | namespace gymlocator.Core.Shopping.Models 8 | { 9 | using Microsoft.Rest; 10 | using Newtonsoft.Json; 11 | using System.Linq; 12 | 13 | public partial class ShoppingList 14 | { 15 | /// 16 | /// Initializes a new instance of the ShoppingList class. 17 | /// 18 | public ShoppingList() 19 | { 20 | CustomInit(); 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the ShoppingList class. 25 | /// 26 | public ShoppingList(int id, System.DateTime created, bool completed, System.DateTime done, string name, int storeID) 27 | { 28 | Id = id; 29 | Created = created; 30 | Completed = completed; 31 | Done = done; 32 | Name = name; 33 | StoreID = storeID; 34 | CustomInit(); 35 | } 36 | 37 | /// 38 | /// An initialization method that performs custom operations like setting defaults 39 | /// 40 | partial void CustomInit(); 41 | 42 | /// 43 | /// 44 | [JsonProperty(PropertyName = "id")] 45 | public int Id { get; set; } 46 | 47 | /// 48 | /// 49 | [JsonProperty(PropertyName = "created")] 50 | public System.DateTime Created { get; set; } 51 | 52 | /// 53 | /// 54 | [JsonProperty(PropertyName = "completed")] 55 | public bool Completed { get; set; } 56 | 57 | /// 58 | /// 59 | [JsonProperty(PropertyName = "done")] 60 | public System.DateTime Done { get; set; } 61 | 62 | /// 63 | /// 64 | [JsonProperty(PropertyName = "name")] 65 | public string Name { get; set; } 66 | 67 | /// 68 | /// 69 | [JsonProperty(PropertyName = "storeID")] 70 | public int StoreID { get; set; } 71 | 72 | /// 73 | /// Validate the object. 74 | /// 75 | /// 76 | /// Thrown if validation fails 77 | /// 78 | public virtual void Validate() 79 | { 80 | if (Name == null) 81 | { 82 | throw new ValidationException(ValidationRules.CannotBeNull, "Name"); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator.Core/gymlocator.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/App.xaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 11 | 21 | 31 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using TinyCacheLib; 3 | using TinyTranslations; 4 | using TinyTranslations.Forms; 5 | using Xamarin.Forms; 6 | 7 | namespace gymlocator 8 | { 9 | public partial class App : Application 10 | { 11 | TranslationHelper Translator; 12 | 13 | public App() 14 | { 15 | InitializeComponent(); 16 | 17 | TinyPubSubLib.TinyPubSubForms.Init(this); 18 | Translator = new TranslationHelper(new System.Uri("http://tinytranslation.azurewebsites.net")); 19 | var tempLocale = "es"; 20 | ansExtension.Translator = Translator; 21 | var oldMethod = Translator.FetchLanguageMethod; 22 | Translator.FetchLanguageMethod = async (locale) => await TinyCache.RunAsync("trans-"+tempLocale, () => 23 | { 24 | return oldMethod(locale); 25 | }); 26 | Translator.Init(tempLocale); 27 | 28 | MainPage = new NavigationPage(new Views.GymListView()); 29 | } 30 | 31 | protected override void OnStart() 32 | { 33 | // Handle when your app starts 34 | } 35 | 36 | protected override void OnSleep() 37 | { 38 | // Handle when your app sleeps 39 | } 40 | 41 | protected override void OnResume() 42 | { 43 | // Handle when your app resumes 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Controls/DrawerControl.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 21 | 29 | 34 | 44 | 45 | 46 | 47 | 53 | 54 | 56 | 58 | 59 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Controls/DrawerControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using gymlocator.Rest.Models; 3 | using gymlocator.ViewModels; 4 | using TinyControls; 5 | using Xamarin.Forms; 6 | 7 | namespace gymlocator.Controls 8 | { 9 | public partial class CustomDrawerControl : DrawerControl 10 | { 11 | public CustomDrawerControl() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | 17 | public EventHandler OnGymSelected; 18 | public EventHandler OnSearchFocus; 19 | 20 | void Handle_TextChanged(object sender, TextChangedEventArgs e) 21 | { 22 | var vm = BindingContext as GymViewModel; 23 | vm.FilterGyms(e.NewTextValue); 24 | } 25 | 26 | void Handle_ItemSelected(object sender, SelectedItemChangedEventArgs e) 27 | { 28 | if (e.SelectedItem != null) 29 | { 30 | OnGymSelected?.Invoke(this, e.SelectedItem as Gym); 31 | gymlist.SelectedItem = null; 32 | } 33 | } 34 | 35 | void Handle_Focused(object sender, FocusEventArgs e) 36 | { 37 | OnSearchFocus?.Invoke(sender, e); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Controls/RepeaterView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Collections.Specialized; 5 | using Xamarin.Forms; 6 | 7 | namespace gymlocator.Controls 8 | { 9 | 10 | public delegate void RepeaterViewItemAddedEventHandler(object sender, RepeaterViewItemAddedEventArgs args); 11 | 12 | // in lieu of an actual Xamarin Forms ItemsControl, this is a heavily modified version of code from https://forums.xamarin.com/discussion/21635/xforms-needs-an-itemscontrol 13 | public class RepeaterView : StackLayout 14 | { 15 | public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create( 16 | "ItemSource", 17 | typeof(IEnumerable), 18 | typeof(RepeaterView), 19 | new List(), 20 | BindingMode.OneWay, 21 | propertyChanged: ItemsChanged); 22 | 23 | public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create( 24 | "ItemTemplate", 25 | typeof(DataTemplate), 26 | typeof(RepeaterView), 27 | default(DataTemplate)); 28 | 29 | public event RepeaterViewItemAddedEventHandler ItemCreated; 30 | 31 | public IEnumerable ItemsSource 32 | { 33 | get { return (IEnumerable)GetValue(ItemsSourceProperty); } 34 | set { SetValue(ItemsSourceProperty, value); } 35 | } 36 | 37 | public DataTemplate ItemTemplate 38 | { 39 | get 40 | { 41 | return (DataTemplate)GetValue(ItemTemplateProperty); 42 | } 43 | set { SetValue(ItemTemplateProperty, value); } 44 | } 45 | 46 | private static void ItemsChanged(BindableObject bindable, object oldValue, object newValue) 47 | { 48 | try 49 | { 50 | var control = (RepeaterView)bindable; 51 | var oldObservableCollection = oldValue as INotifyCollectionChanged; 52 | 53 | if (oldObservableCollection != null) 54 | { 55 | oldObservableCollection.CollectionChanged -= control.OnItemsSourceCollectionChanged; 56 | } 57 | 58 | var newObservableCollection = newValue as INotifyCollectionChanged; 59 | 60 | if (newObservableCollection != null) 61 | { 62 | newObservableCollection.CollectionChanged += control.OnItemsSourceCollectionChanged; 63 | } 64 | 65 | control.Children.Clear(); 66 | 67 | if (newValue != null) 68 | { 69 | foreach (var item in (IEnumerable)newValue) 70 | { 71 | var view = control.CreateChildViewFor(item); 72 | control.Children.Add(view); 73 | control.OnItemCreated(view); 74 | } 75 | } 76 | 77 | control.UpdateChildrenLayout(); 78 | control.InvalidateLayout(); 79 | } 80 | catch (Exception e) 81 | { 82 | throw; 83 | } 84 | } 85 | 86 | protected virtual void OnItemCreated(View view) => 87 | this.ItemCreated?.Invoke(this, new RepeaterViewItemAddedEventArgs(view, view.BindingContext)); 88 | 89 | private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 90 | { 91 | var invalidate = false; 92 | 93 | if (e.OldItems != null) 94 | { 95 | this.Children.RemoveAt(e.OldStartingIndex); 96 | invalidate = true; 97 | } 98 | 99 | if (e.NewItems != null) 100 | { 101 | for (var i = 0; i < e.NewItems.Count; ++i) 102 | { 103 | var item = e.NewItems[i]; 104 | var view = this.CreateChildViewFor(item); 105 | 106 | this.Children.Insert(i + e.NewStartingIndex, view); 107 | OnItemCreated(view); 108 | } 109 | 110 | invalidate = true; 111 | } 112 | 113 | if (invalidate) 114 | { 115 | this.UpdateChildrenLayout(); 116 | this.InvalidateLayout(); 117 | } 118 | } 119 | 120 | private View CreateChildViewFor(object item) 121 | { 122 | this.ItemTemplate.SetValue(BindableObject.BindingContextProperty, item); 123 | return (View)this.ItemTemplate.CreateContent(); 124 | } 125 | } 126 | 127 | public class RepeaterViewItemAddedEventArgs : EventArgs 128 | { 129 | private readonly View view; 130 | private readonly object model; 131 | 132 | public RepeaterViewItemAddedEventArgs(View view, object model) 133 | { 134 | this.view = view; 135 | this.model = model; 136 | } 137 | 138 | public View View => this.view; 139 | 140 | public object Model => this.model; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Controls/RoundedBoxView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xamarin.Forms; 3 | 4 | namespace gymlocator.Controls 5 | { 6 | public class RoundedBoxView : BoxView 7 | { 8 | public static readonly BindableProperty CornerRadiusProperty = 9 | BindableProperty.Create("CornerRadius", typeof(float), typeof(RoundedBoxView), 0.0f, validateValue: IsValidRadius); 10 | 11 | public float CornerRadius 12 | { 13 | get 14 | { 15 | return (float)GetValue(CornerRadiusProperty); 16 | } 17 | set 18 | { 19 | SetValue(CornerRadiusProperty, value); 20 | } 21 | } 22 | 23 | static bool IsValidRadius(BindableObject bindable, object value) 24 | { 25 | float result; 26 | bool isFloat = float.TryParse(value.ToString(), out result); 27 | return isFloat && (result >= 0 && result <= 1000); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Controls/SearchBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xamarin.Forms; 3 | 4 | namespace gymlocator.Controls 5 | { 6 | public class SearchBox : SearchBar 7 | { 8 | public SearchBox() 9 | { 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/ViewModels/GymDetailViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Input; 4 | using gymlocator.Rest.Models; 5 | using Xamarin.Forms; 6 | 7 | namespace gymlocator.ViewModels 8 | { 9 | public class GymDetailViewModel : ViewModelBase 10 | { 11 | public GymDetailViewModel(ContentPage page, Gym selectedGym) : base(page) 12 | { 13 | if (selectedGym != null) 14 | { 15 | currentGym = selectedGym; 16 | } 17 | } 18 | 19 | private Gym currentGym; 20 | 21 | public string Name { get; set; } 22 | public string Description { get; set; } 23 | public string Email { get; set; } 24 | public string Phone { get; set; } 25 | 26 | public string Address { get; set; } 27 | public string Zip { get; set; } 28 | public string City { get; set; } 29 | 30 | public IList Features { get; set; } 31 | public IList PersonalTrainers { get; set; } 32 | 33 | public ICommand OpenFacebook => new Command(() => 34 | { 35 | Device.OpenUri(new Uri(currentGym.FacebookPage)); 36 | }); 37 | 38 | public override void OnFirstAppear() 39 | { 40 | base.OnFirstAppear(); 41 | if (currentGym != null) 42 | { 43 | Device.BeginInvokeOnMainThread(() => 44 | { 45 | Name = currentGym.Name; 46 | Title = currentGym.Name; 47 | Description = currentGym.Description; 48 | Address = currentGym.Address.StreetAddress; 49 | Zip = currentGym.Address.PostalCode; 50 | City = currentGym.Address.City; 51 | PersonalTrainers = currentGym.PersonalTrainers; 52 | Features = currentGym.Features; 53 | Email = currentGym.Contact.Email; 54 | Phone = currentGym.Contact.Phone; 55 | }); 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/ViewModels/GymViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Windows.Input; 6 | using gymlocator.Core; 7 | using gymlocator.Rest.Models; 8 | using gymlocator.Views; 9 | using TK.CustomMap; 10 | using Xamarin.Forms; 11 | using Xamarin.Forms.Maps; 12 | 13 | namespace gymlocator.ViewModels 14 | { 15 | 16 | public class GymViewModel : ViewModelBase 17 | { 18 | public GymViewModel(ContentPage page) : base(page) 19 | { 20 | dataModel = new DataStore(); 21 | } 22 | 23 | private DataStore dataModel; 24 | private string currentFilter; 25 | private IList allGyms = new List(); 26 | private bool hasRunInit = false; 27 | private TKCustomMap map; 28 | 29 | public ObservableCollection Gyms { get; set; } = new ObservableCollection(); 30 | public ObservableCollection Pins { get; set; } = new ObservableCollection(); 31 | 32 | public ICommand OpenGym => new Command((arg) => 33 | { 34 | Gym gym = null; 35 | if (arg is TKCustomMapPin pin) 36 | { 37 | gym = Gyms.FirstOrDefault(d => d.Id == pin.ID); 38 | } 39 | else if (arg is Gym g) 40 | { 41 | gym = g; 42 | } 43 | Navigation.PushAsync(new GymDetailView(gym)); 44 | }); 45 | 46 | public ICommand DoRefresh => new Command(async () => 47 | { 48 | IsBusy = true; 49 | var gyms = await dataModel.GetGymsAsync(); 50 | PopulateGyms(gyms); 51 | IsBusy = false; 52 | }); 53 | 54 | public void FilterGyms(string newTextValue) 55 | { 56 | currentFilter = newTextValue.ToLower(); 57 | FilterResults(); 58 | } 59 | 60 | private void PopulateGyms(IList gyms) 61 | { 62 | allGyms = gyms; 63 | Device.BeginInvokeOnMainThread(() => 64 | { 65 | foreach (var gym in gyms) 66 | { 67 | if (!allGyms.Any(d => d.Id == gym.Id)) 68 | { 69 | allGyms.Add(gym); 70 | } 71 | if (gym.Location != null) 72 | { 73 | var pos = new Position(gym.Location.Lat, gym.Location.Lng); 74 | 75 | if (!Pins.Any(d => d.ID == gym.Id)) 76 | { 77 | Pins.Add(new TKCustomMapPin() 78 | { 79 | Position = pos, 80 | ID = gym.Id, 81 | IsVisible = true, 82 | IsCalloutClickable = true, 83 | Title = gym.Name, 84 | ShowCallout = true, 85 | Subtitle = gym.Address.StreetAddress 86 | }); 87 | } 88 | } 89 | } 90 | }); 91 | FilterResults(); 92 | } 93 | 94 | private void FilterResults() 95 | { 96 | var gymsToRemove = Gyms.Select(d => d.Id).ToList(); 97 | foreach (var gym in allGyms) 98 | { 99 | if (Match(gym)) 100 | { 101 | if (gymsToRemove.Contains(gym.Id)) 102 | { 103 | gymsToRemove.Remove(gym.Id); 104 | } 105 | else 106 | { 107 | Gyms.Add(gym); 108 | var gymPin = Pins.FirstOrDefault(); 109 | if (gymPin != null) 110 | { 111 | gymPin.IsVisible = true; 112 | } 113 | } 114 | } 115 | } 116 | foreach(var toremove in gymsToRemove) { 117 | var gym = Gyms.FirstOrDefault(d => d.Id == toremove); 118 | var pinToRemove = Pins.FirstOrDefault(d => d.ID == toremove); 119 | if (gym != null) 120 | Gyms.Remove(gym); 121 | if (pinToRemove != null) 122 | pinToRemove.IsVisible = false; 123 | } 124 | } 125 | 126 | private bool Match(Gym gym) 127 | { 128 | if (string.IsNullOrWhiteSpace(currentFilter)) 129 | return true; 130 | return (gym.Name.ToLower().Contains(currentFilter)); 131 | } 132 | 133 | public override void OnFirstAppear() 134 | { 135 | base.OnFirstAppear(); 136 | Init(); 137 | } 138 | 139 | public async void Init(TKCustomMap map = null) 140 | { 141 | //var ss = new ShoppingService(); 142 | //var lists = await ss.GetShoppingLists(); 143 | if (map != null) 144 | this.map = map; 145 | if (!hasRunInit) 146 | { 147 | hasRunInit = true; 148 | var gyms = await dataModel.GetGymsAsync(); 149 | 150 | if (map != null) 151 | { 152 | map.CustomPins = Pins; 153 | map.CalloutClicked += (sender, e) => OpenGym.Execute(e.Value); 154 | } 155 | if (gyms != null && gyms.Any()) 156 | { 157 | PopulateGyms(gyms); 158 | } 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Xamarin.Forms; 3 | 4 | namespace gymlocator.ViewModels 5 | { 6 | [PropertyChanged.AddINotifyPropertyChangedInterface] 7 | public class ViewModelBase 8 | { 9 | public ViewModelBase(ContentPage page) 10 | { 11 | page.BindingContext = this; 12 | Navigation = page.Navigation; 13 | } 14 | 15 | private Dictionary translations = new Dictionary(); 16 | private bool hasAppeared; 17 | 18 | public string this[string translationKey] 19 | { 20 | get 21 | { 22 | if (translations.ContainsKey(translationKey)) 23 | return translations[translationKey]; 24 | else 25 | { 26 | translations.Add(translationKey,translationKey); 27 | } 28 | return translationKey; 29 | } 30 | } 31 | 32 | public INavigation Navigation { get; internal set; } 33 | 34 | public string Title { get; set; } = "No name"; 35 | 36 | public bool IsBusy { get; set; } 37 | 38 | public virtual void OnAppear() 39 | { 40 | if (!hasAppeared) 41 | { 42 | hasAppeared = true; 43 | OnFirstAppear(); 44 | } 45 | } 46 | 47 | public virtual void OnFirstAppear() 48 | { 49 | 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Views/GymDetailView.xaml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 13 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Views/GymDetailView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using gymlocator.Rest.Models; 4 | using gymlocator.ViewModels; 5 | using Xamarin.Forms; 6 | 7 | namespace gymlocator.Views 8 | { 9 | public partial class GymDetailView : ContentPage 10 | { 11 | private GymDetailViewModel viewModel; 12 | 13 | public GymDetailView(Gym gym) 14 | { 15 | InitializeComponent(); 16 | viewModel = new GymDetailViewModel(this, gym); 17 | } 18 | 19 | protected override void OnAppearing() 20 | { 21 | base.OnAppearing(); 22 | viewModel.OnAppear(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Views/GymListView.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 13 | 15 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/Views/GymListView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using gymlocator.ViewModels; 4 | using Plugin.Geolocator; 5 | using Xamarin.Forms; 6 | using Xamarin.Forms.Maps; 7 | 8 | namespace gymlocator.Views 9 | { 10 | public partial class GymListView : ContentPage 11 | { 12 | private bool keyboardOpen; 13 | 14 | private GymViewModel viewModel { get; set; } 15 | private Distance defaultDistance = Distance.FromKilometers(1); 16 | 17 | public GymListView() 18 | { 19 | InitializeComponent(); 20 | viewModel = new GymViewModel(this); 21 | NavigationPage.SetHasNavigationBar(this, false); 22 | 23 | BindingContext = viewModel; 24 | 25 | keyboardOpen = false; 26 | sliderOverlay.OnSizeChange += (sender, e) => 27 | { 28 | if (e.Old > e.New) 29 | { 30 | HideKeyboard(); 31 | } 32 | }; 33 | 34 | sliderOverlay.OnGymSelected += (sender, e) => 35 | { 36 | var moveToPos = new Xamarin.Forms.Maps.Position(e.Location.Lat, e.Location.Lng); 37 | map.MoveToRegion(MapSpan.FromCenterAndRadius(moveToPos, defaultDistance)); 38 | HideKeyboard(); 39 | ovelayController.Minimize(sliderOverlay); 40 | }; 41 | 42 | sliderOverlay.OnSearchFocus += (sender, e) => 43 | { 44 | keyboardOpen = true; 45 | ovelayController.MaximizeOverlay(sliderOverlay); 46 | }; 47 | SetupLocation(); 48 | } 49 | 50 | private void HideKeyboard() 51 | { 52 | if (keyboardOpen) 53 | { 54 | keyboardOpen = false; 55 | TinyPubSubLib.TinyPubSub.Publish("HideKeyboard"); 56 | } 57 | } 58 | 59 | 60 | private void SetupLocation() 61 | { 62 | var geo = CrossGeolocator.Current; 63 | geo.PositionChanged += (sender, e) => 64 | { 65 | var pos = new Position(e.Position.Latitude, e.Position.Longitude); 66 | map.MoveToMapRegion(MapSpan.FromCenterAndRadius(pos, defaultDistance)); 67 | }; 68 | geo.StartListeningAsync(TimeSpan.FromMinutes(2), 150); 69 | } 70 | 71 | protected override void OnAppearing() 72 | { 73 | base.OnAppearing(); 74 | //viewModel.OnAppear(); 75 | viewModel.Init(map); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/gymlocator.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | {293A3DE2-7FAC-481A-807E-FA71966F7C21} 7 | 8 | 9 | gymlocator 10 | 11 | 12 | 13 | 14 | Designer 15 | MSBuild:UpdateDesignTimeXaml 16 | 17 | 18 | Designer 19 | MSBuild:UpdateDesignTimeXaml 20 | 21 | 22 | Designer 23 | MSBuild:UpdateDesignTimeXaml 24 | 25 | 26 | 27 | 28 | App.xaml 29 | 30 | 31 | 32 | GymListView.xaml 33 | 34 | 35 | GymDetailView.xaml 36 | 37 | 38 | 39 | 40 | 41 | DrawerControl.xaml 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/samples/Forms/gymlocator/gymlocator.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {293A3DE2-7FAC-481A-807E-FA71966F7C21} 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Foundation; 6 | using TK.CustomMap.iOSUnified; 7 | using UIKit; 8 | 9 | namespace gymlocator.iOS 10 | { 11 | [Register("AppDelegate")] 12 | public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate 13 | { 14 | public override bool FinishedLaunching(UIApplication app, NSDictionary options) 15 | { 16 | UINavigationBar.Appearance.Translucent = true; 17 | UINavigationBar.Appearance.BarStyle = UIBarStyle.Default; 18 | global::Xamarin.Forms.Forms.Init(); 19 | global::Xamarin.FormsMaps.Init(); 20 | TKCustomMapRenderer.InitMapRenderer(); 21 | LoadApplication(new App()); 22 | 23 | return base.FinishedLaunching(app, options); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/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 | "size" : "24x24", 95 | "idiom" : "watch", 96 | "scale" : "2x", 97 | "role" : "notificationCenter", 98 | "subtype" : "38mm" 99 | }, 100 | { 101 | "size" : "27.5x27.5", 102 | "idiom" : "watch", 103 | "scale" : "2x", 104 | "role" : "notificationCenter", 105 | "subtype" : "42mm" 106 | }, 107 | { 108 | "size" : "29x29", 109 | "idiom" : "watch", 110 | "role" : "companionSettings", 111 | "scale" : "2x" 112 | }, 113 | { 114 | "size" : "29x29", 115 | "idiom" : "watch", 116 | "role" : "companionSettings", 117 | "scale" : "3x" 118 | }, 119 | { 120 | "size" : "40x40", 121 | "idiom" : "watch", 122 | "scale" : "2x", 123 | "role" : "appLauncher", 124 | "subtype" : "38mm" 125 | }, 126 | { 127 | "size" : "44x44", 128 | "idiom" : "watch", 129 | "scale" : "2x", 130 | "role" : "longLook", 131 | "subtype" : "42mm" 132 | }, 133 | { 134 | "size" : "86x86", 135 | "idiom" : "watch", 136 | "scale" : "2x", 137 | "role" : "quickLook", 138 | "subtype" : "38mm" 139 | }, 140 | { 141 | "size" : "98x98", 142 | "idiom" : "watch", 143 | "scale" : "2x", 144 | "role" : "quickLook", 145 | "subtype" : "42mm" 146 | }, 147 | { 148 | "idiom" : "mac", 149 | "size" : "16x16", 150 | "scale" : "1x" 151 | }, 152 | { 153 | "idiom" : "mac", 154 | "size" : "16x16", 155 | "scale" : "2x" 156 | }, 157 | { 158 | "idiom" : "mac", 159 | "size" : "32x32", 160 | "scale" : "1x" 161 | }, 162 | { 163 | "idiom" : "mac", 164 | "size" : "32x32", 165 | "scale" : "2x" 166 | }, 167 | { 168 | "idiom" : "mac", 169 | "size" : "128x128", 170 | "scale" : "1x" 171 | }, 172 | { 173 | "idiom" : "mac", 174 | "size" : "128x128", 175 | "scale" : "2x" 176 | }, 177 | { 178 | "idiom" : "mac", 179 | "size" : "256x256", 180 | "scale" : "1x" 181 | }, 182 | { 183 | "idiom" : "mac", 184 | "size" : "256x256", 185 | "scale" : "2x" 186 | }, 187 | { 188 | "idiom" : "mac", 189 | "size" : "512x512", 190 | "scale" : "1x" 191 | }, 192 | { 193 | "idiom" : "mac", 194 | "size" : "512x512", 195 | "scale" : "2x" 196 | } 197 | ], 198 | "info" : { 199 | "version" : 1, 200 | "author" : "xcode" 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /src/samples/Forms/iOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | gymlocator 7 | CFBundleName 8 | gymlocator 9 | CFBundleIdentifier 10 | com.f24s.gymlocator 11 | CFBundleShortVersionString 12 | 1.0 13 | CFBundleVersion 14 | 1.0 15 | LSRequiresIPhoneOS 16 | 17 | MinimumOSVersion 18 | 8.0 19 | UIDeviceFamily 20 | 21 | 1 22 | 2 23 | 24 | UILaunchStoryboardName 25 | LaunchScreen 26 | UIRequiredDeviceCapabilities 27 | 28 | armv7 29 | 30 | UISupportedInterfaceOrientations 31 | 32 | UIInterfaceOrientationPortrait 33 | UIInterfaceOrientationLandscapeLeft 34 | UIInterfaceOrientationLandscapeRight 35 | 36 | UISupportedInterfaceOrientations~ipad 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationPortraitUpsideDown 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | XSAppIconAssets 44 | Assets.xcassets/AppIcon.appiconset 45 | NSLocationAlwaysUsageDescription 46 | Can we use your location 47 | NSLocationWhenInUseUsageDescription 48 | We are using your location 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/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 | -------------------------------------------------------------------------------- /src/samples/Forms/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 gymlocator.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/samples/Forms/iOS/Renderers/CustomBoxViewRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using gymlocator.Controls; 3 | using gymlocator.iOS.Renderers; 4 | using Xamarin.Forms; 5 | using Xamarin.Forms.Platform.iOS; 6 | 7 | [assembly: ExportRenderer(typeof(RoundedBoxView), typeof(CustomBoxViewRenderer))] 8 | 9 | namespace gymlocator.iOS.Renderers 10 | { 11 | public class CustomBoxViewRenderer : BoxRenderer 12 | { 13 | protected override void OnElementChanged(ElementChangedEventArgs e) 14 | { 15 | base.OnElementChanged(e); 16 | if (e.NewElement is RoundedBoxView rbc) 17 | { 18 | SetRadius(rbc); 19 | } 20 | } 21 | 22 | private void SetRadius(RoundedBoxView rbc) 23 | { 24 | Layer.MasksToBounds = (rbc.CornerRadius > 0); 25 | Layer.CornerRadius = rbc.CornerRadius; 26 | } 27 | 28 | protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 29 | { 30 | base.OnElementPropertyChanged(sender, e); 31 | if (Element is RoundedBoxView rbc && e.PropertyName=="CornerRadius") { 32 | SetRadius(rbc); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/Renderers/CustomEntryRenderer.cs: -------------------------------------------------------------------------------- 1 | using gymlocator.Controls; 2 | using gymlocator.iOS.Renderers; 3 | using TinyPubSubLib; 4 | using UIKit; 5 | using Xamarin.Forms; 6 | using Xamarin.Forms.Platform.iOS; 7 | 8 | [assembly: ExportRenderer(typeof(SearchBox), typeof(SearchBoxEntryRenderer))] 9 | 10 | namespace gymlocator.iOS.Renderers 11 | { 12 | public class SearchBoxEntryRenderer : SearchBarRenderer 13 | { 14 | //protected override void OnElementChanged(ElementChangedEventArgs e) 15 | //{ 16 | // base.OnElementChanged(e); 17 | // Control.ReturnKeyType = UIReturnKeyType.Search; 18 | // Control.ClearButtonMode = UITextFieldViewMode.Always; 19 | // Control.BorderStyle = UITextBorderStyle.None; 20 | // Control.TextAlignment = UITextAlignment.Center; 21 | // Layer.CornerRadius = 8; 22 | // Layer.MasksToBounds = true; 23 | // Layer.BorderColor = UIColor.Clear.CGColor; 24 | 25 | //} 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/Renderers/LargeTabbedPageRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using gymlocator.Controls; 3 | using gymlocator.iOS.Renderers; 4 | using UIKit; 5 | using Xamarin.Forms; 6 | using Xamarin.Forms.Platform.iOS; 7 | 8 | [assembly: ExportRenderer(typeof(Page), typeof(LargeTabbedPageRenderer))] 9 | 10 | namespace gymlocator.iOS.Renderers 11 | { 12 | public class LargeTabbedPageRenderer : PageRenderer 13 | { 14 | public LargeTabbedPageRenderer() { 15 | TinyPubSubLib.TinyPubSub.Subscribe(this, "HideKeyboard",()=> { 16 | View.EndEditing(true); 17 | }); 18 | } 19 | 20 | public override void ViewWillAppear(bool animated) 21 | { 22 | base.ViewWillAppear(animated); 23 | if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0) && NavigationController != null && NavigationController.NavigationBar != null) 24 | { 25 | NavigationController.NavigationBar.PrefersLargeTitles = !NavigationController.NavigationBar.Hidden; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/Renderers/TabbedPageRenderer.cs: -------------------------------------------------------------------------------- 1 | using gymlocator.Controls; 2 | using gymlocator.iOS.Renderers; 3 | using UIKit; 4 | using Xamarin.Forms; 5 | using Xamarin.Forms.Platform.iOS; 6 | 7 | [assembly: ExportRenderer(typeof(TabbedPage), typeof(TabbedPageRenderer))] 8 | 9 | namespace gymlocator.iOS.Renderers 10 | { 11 | public class TabbedPageRenderer : TabbedRenderer 12 | { 13 | protected override void OnElementChanged(VisualElementChangedEventArgs e) 14 | { 15 | base.OnElementChanged(e); 16 | UITabBarItem.Appearance.TitlePositionAdjustment = new UIOffset(0, -2f); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/samples/Forms/iOS/packages.config: -------------------------------------------------------------------------------- 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 | --------------------------------------------------------------------------------