Skip to content

Commit

Permalink
iOS suggestions work (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
enisn authored Mar 4, 2019
1 parent 70eedd3 commit 927cc8f
Show file tree
Hide file tree
Showing 19 changed files with 1,180 additions and 10 deletions.
5 changes: 2 additions & 3 deletions InputKit/InputKit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageVersion>1.0.0.0</PackageVersion>
<PackOnBuild>true</PackOnBuild>
<NeutralLanguage>en</NeutralLanguage>
<LangVersion>7.1</LangVersion>
<LangVersion>7.3</LangVersion>
<DefineConstants>$(DefineConstants);</DefineConstants>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<GenerateDocumentationFile Condition=" '$(Configuration)' == 'Release' ">true</GenerateDocumentationFile>
Expand Down Expand Up @@ -124,8 +124,7 @@
<EmbeddedResource Include="Shared\Resources\**\*.png" />
<EmbeddedResource Include="Shared\Resources\**\*.jpg" />
</ItemGroup>



<!--
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
<Compile Include="Platforms\DotNet\**\*.cs" />
Expand Down
219 changes: 219 additions & 0 deletions InputKit/Platforms/Droid/AutoCompleteViewRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
using System.ComponentModel;
using Android.Content;
using Android.Content.Res;
using Android.Support.Design.Widget;
using Android.Support.V4.View;
using Android.Support.V7.Widget;
using Android.Text;
using Android.Text.Method;
using Android.Util;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Java.Lang;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Application = Android.App.Application;
using Color = Xamarin.Forms.Color;
using AColor = Android.Graphics.Color;
using FormsAppCompat = Xamarin.Forms.Platform.Android.AppCompat;
using Plugin.InputKit.Shared.Controls;
using Plugin.InputKit.Platforms.Droid;
using System.Collections.Specialized;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Collections;
using Android.Graphics.Drawables;

[assembly: ExportRenderer(typeof(AutoCompleteView), typeof(AutoCompleteViewRenderer))]
namespace Plugin.InputKit.Platforms.Droid
{
public class AutoCompleteViewRenderer : FormsAppCompat.ViewRenderer<AutoCompleteView, TextInputLayout>
{
public AutoCompleteViewRenderer(Context context) : base(context)
{
}

private AppCompatAutoCompleteTextView AutoComplete => Control?.EditText as AppCompatAutoCompleteTextView;

protected override TextInputLayout CreateNativeControl()
{
var textInputLayout = new TextInputLayout(Context);
var autoComplete = new AppCompatAutoCompleteTextView(Context)
{
BackgroundTintList = ColorStateList.ValueOf(GetPlaceholderColor())
};

GradientDrawable gd = new GradientDrawable();
gd.SetColor(global::Android.Graphics.Color.Transparent);
autoComplete.SetBackground(gd);

textInputLayout.AddView(autoComplete);
return textInputLayout;
}

protected override void OnElementChanged(ElementChangedEventArgs<AutoCompleteView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// unsubscribe
AutoComplete.ItemClick -= AutoCompleteOnItemSelected;
var elm = e.OldElement;
elm.CollectionChanged -= ItemsSourceCollectionChanged;
}

if (e.NewElement != null)
{
SetNativeControl(CreateNativeControl());
// subscribe
SetItemsSource();
SetThreshold();
KillPassword();
AutoComplete.ItemClick += AutoCompleteOnItemSelected;
var elm = e.NewElement;
elm.CollectionChanged += ItemsSourceCollectionChanged;
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);

if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
KillPassword();
if (e.PropertyName == AutoCompleteView.ItemsSourceProperty.PropertyName)
SetItemsSource();
else if (e.PropertyName == AutoCompleteView.ThresholdProperty.PropertyName)
SetThreshold();
}

private void AutoCompleteOnItemSelected(object sender, AdapterView.ItemClickEventArgs args)
{
var view = (AutoCompleteTextView)sender;
var selectedItemArgs = new SelectedItemChangedEventArgs(view.Text);
var element = (AutoCompleteView)Element;
element.OnItemSelectedInternal(Element, selectedItemArgs);
}

private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
var element = (AutoCompleteView)Element;
ResetAdapter(element);
}

private void KillPassword()
{
if (Element.IsPassword)
throw new NotImplementedException("Cannot set IsPassword on a AutoComplete");
}

private void ResetAdapter(AutoCompleteView element)
{
var adapter = new BoxArrayAdapter(Context,
Android.Resource.Layout.SimpleDropDownItem1Line,
element.ItemsSource.ToList(),
element.SortingAlgorithm);
AutoComplete.Adapter = adapter;
adapter.NotifyDataSetChanged();
}

private void SetItemsSource()
{
var element = (AutoCompleteView)Element;
if (element.ItemsSource == null) return;

ResetAdapter(element);
}

private void SetThreshold()
{
var element = (AutoCompleteView)Element;
AutoComplete.Threshold = element.Threshold;
}

#region Section 2
protected AColor GetPlaceholderColor() => Element.PlaceholderColor.ToAndroid(Color.FromHex("#80000000"));
#endregion
}

internal class BoxArrayAdapter : ArrayAdapter
{
private readonly IList<string> _objects;
private readonly Func<string, ICollection<string>, ICollection<string>> _sortingAlgorithm;

public BoxArrayAdapter(
Context context,
int textViewResourceId,
List<string> objects,
Func<string, ICollection<string>, ICollection<string>> sortingAlgorithm) : base(context, textViewResourceId, objects)
{
_objects = objects;
_sortingAlgorithm = sortingAlgorithm;
}

public override Filter Filter
{
get
{
return new CustomFilter(_sortingAlgorithm) { Adapter = this, Originals = _objects };
}
}
}

internal class CustomFilter : Filter
{
private readonly Func<string, ICollection<string>, ICollection<string>> _sortingAlgorithm;

public CustomFilter(Func<string, ICollection<string>, ICollection<string>> sortingAlgorithm)
{
_sortingAlgorithm = sortingAlgorithm;
}

public BoxArrayAdapter Adapter { private get; set; }
public IList<string> Originals { get; set; }

protected override FilterResults PerformFiltering(ICharSequence constraint)
{
var results = new FilterResults();
if (constraint == null || constraint.Length() == 0)
{
results.Values = new Java.Util.ArrayList(Originals.ToList());
results.Count = Originals.Count;
}
else
{
var values = new Java.Util.ArrayList();
var sorted = _sortingAlgorithm(constraint.ToString(), Originals).ToList();

for (var index = 0; index < sorted.Count; index++)
{
var item = sorted[index];
values.Add(item);
}

results.Values = values;
results.Count = sorted.Count;
}

return results;
}

protected override void PublishResults(ICharSequence constraint, FilterResults results)
{
if (results.Count == 0)
Adapter.NotifyDataSetInvalidated();
else
{
Adapter.Clear();
var vals = (Java.Util.ArrayList)results.Values;
foreach (var val in vals.ToArray())
{
Adapter.Add(val);
}
Adapter.NotifyDataSetChanged();
}
}
}
}
122 changes: 122 additions & 0 deletions InputKit/Platforms/iOS/AutoCompleteViewRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using CoreGraphics;
using Plugin.InputKit.Platforms.iOS;
using Plugin.InputKit.Platforms.iOS.Controls;
using Plugin.InputKit.Platforms.iOS.Helpers;
using Plugin.InputKit.Shared.Controls;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(AutoCompleteView), typeof(AutoCompleteViewRenderer))]
namespace Plugin.InputKit.Platforms.iOS
{
public class AutoCompleteViewRenderer : ViewRenderer<AutoCompleteView, UITextField>
{
private MbAutoCompleteTextField NativeControl => (MbAutoCompleteTextField)Control;
private AutoCompleteView AutoCompleteEntry => (AutoCompleteView)Element;

public AutoCompleteViewRenderer()
{
// ReSharper disable once VirtualMemberCallInContructor
Frame = new RectangleF(0, 20, 320, 40);
}

protected override UITextField CreateNativeControl()
{
var element = (AutoCompleteView)Element;
var view = new MbAutoCompleteTextField
{
AutoCompleteViewSource = new MbAutoCompleteDefaultDataSource(),
SortingAlgorithm = element.SortingAlgorithm
};
view.AutoCompleteViewSource.Selected += AutoCompleteViewSourceOnSelected;
return view;
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
var scrollView = GetParentScrollView(Control);
var ctrl = UIApplication.SharedApplication.GetTopViewController();
NativeControl.Draw(ctrl, Layer, scrollView);
}

protected override void OnElementChanged(ElementChangedEventArgs<AutoCompleteView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// unsubscribe
NativeControl.AutoCompleteViewSource.Selected -= AutoCompleteViewSourceOnSelected;
var elm = (AutoCompleteView)e.OldElement;
elm.CollectionChanged -= ItemsSourceCollectionChanged;
}

if (e.NewElement != null)
{
SetNativeControl(CreateNativeControl());
SetItemsSource();
SetThreshold();
KillPassword();

var elm = (AutoCompleteView)e.NewElement;
elm.CollectionChanged += ItemsSourceCollectionChanged;
}
}

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);

if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
KillPassword();
if (e.PropertyName == AutoCompleteView.ItemsSourceProperty.PropertyName)
SetItemsSource();
else if (e.PropertyName == AutoCompleteView.ThresholdProperty.PropertyName)
SetThreshold();
}

private void SetThreshold()
{
NativeControl.Threshold = AutoCompleteEntry.Threshold;
}

private void SetItemsSource()
{
if (AutoCompleteEntry.ItemsSource != null)
{
var items = AutoCompleteEntry.ItemsSource.ToList();
NativeControl.UpdateItems(items);
}
}

private void KillPassword()
{
if (Element.IsPassword)
throw new NotImplementedException("Cannot set IsPassword on a AutoCompleteEntry");
}

private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
SetItemsSource();
}

private static UIScrollView GetParentScrollView(UIView element)
{
if (element.Superview == null) return null;
var scrollView = element.Superview as UIScrollView;
return scrollView ?? GetParentScrollView(element.Superview);
}

private void AutoCompleteViewSourceOnSelected(object sender, SelectedItemChangedEventArgs args)
{
AutoCompleteEntry.OnItemSelectedInternal(Element, args);
}
}
}
28 changes: 28 additions & 0 deletions InputKit/Platforms/iOS/Controls/MbAutoCompleteTableView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
using UIKit;

namespace Plugin.InputKit.Platforms.iOS.Controls
{
internal class MbAutoCompleteTableView : UITableView
{
private readonly UIScrollView _parentScrollView;

public MbAutoCompleteTableView(UIScrollView parentScrollView)
{
_parentScrollView = parentScrollView;
}

public override bool Hidden
{
get { return base.Hidden; }
set
{
base.Hidden = value;
if (_parentScrollView == null) return;
_parentScrollView.DelaysContentTouches = !value;
}
}
}
}
Loading

0 comments on commit 927cc8f

Please sign in to comment.