AutomationPeer.GetChildrenCore () only reports first child to VisualStudio.TestTools - xamarin

I'm not able to override GetChildrenCore correctly. I use this for a Canvas to get information about it's children (Line, Rectangle).
The output correctly indicates the first child but misses the second. Even though the Canvas already contains both.
Custom Canvas
Custom Line Childs of Canvas parent: 2
Instead it should be like this:
Custom Canvas
Custom Line Childs of Canvas parent: 2
Custom Rectangle Childs of Canvas parent: 2
App side side:
public class ElementAP : FrameworkElementAutomationPeer
{
private FrameworkElement Owner = null;
private Int32 Count = 0;
public ElementAP(FrameworkElement owner, Int32 count) : base (owner)
{
Owner = owner;
Count = count;
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
protected override string GetClassNameCore()
{
return $"{Owner.GetType().Name} Childs of Canvas parent: {Count}";
}
}
public class CanvasAP : FrameworkElementAutomationPeer
{
public CanvasAP(Windows.UI.Xaml.Controls.Canvas owner) : base(owner)
{
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
protected override string GetClassNameCore()
{
return "Canvas";
}
protected override IList<AutomationPeer> GetChildrenCore()
{
var owner = (Windows.UI.Xaml.Controls.Canvas)Owner;
var list = new List<AutomationPeer> ();
foreach (var child in owner.Children)
{
var peer = new ElementAP(child as FrameworkElement, owner.Children.Count);
list.Add(peer);
}
return list;
}
}
UI Testing side:
private static string WalkTree(UITestControl element, Int32 level = 0)
{
var children = element.GetChildren();
var str = "";
foreach (var c in children)
{
str += GetElementString(c, level);
str += WalkTree(c, level + 1);
}
return str;
}
private static string GetElementString(UITestControl element, Int32 level = 0)
{
var xaml = element as XamlControl;
var str = "";
for (var i = 0; i < level; i++)
str += " ";
str += $"{element.ControlType} {element.ClassName} {element.Name} {xaml?.AutomationId ?? ""}\n";
return str;
}

I finally found an answer. When using a cache for the children`s AutomationPeers it works perfectly.
public class ElementAP : FrameworkElementAutomationPeer
{
public UIElement Element { get { return Owner; } }
public ElementAP(FrameworkElement owner) : base(owner)
{
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
protected override string GetClassNameCore()
{
return Owner.GetType().Name;
}
}
public class CanvasAP : FrameworkElementAutomationPeer
{
private List<ElementAP> _cachedAutomationPeers = new List<ElementAP>();
public CanvasAP(Windows.UI.Xaml.Controls.Canvas owner) : base(owner)
{
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
protected override string GetClassNameCore()
{
return "Canvas";
}
protected override IList<AutomationPeer> GetChildrenCore()
{
var owner = (Windows.UI.Xaml.Controls.Canvas)Owner;
if (owner.Children.All(c => c is CanvasA))
return base.GetChildrenCore();
var list = new List<ElementAP>();
foreach (var child in owner.Children)
{
var peer = _cachedAutomationPeers.FirstOrDefault(p => p.Element == child) ?? new ElementAP(child as FrameworkElement);
list.Add(peer);
}
_cachedAutomationPeers = list;
return list.Cast<AutomationPeer>().ToList();
}
}

Related

how to implement Android In App BillingClient in Xamarin.Android Asynchronously

I am trying to implement below java code in c# referring to Android documentation
List<String> skuList = new ArrayList<> ();
skuList.add("premium_upgrade");
skuList.add("gas");
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(SkuType.INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
// Process the result.
}
});
I have here 2 questions. I thought that i would run this code on a separate thread than UI thread like below to keep my ui responsive while network connection is done. is that the correct approach? QuerySkuDetailsAsync is called async but doesnt implement as async. how should this be working and how to handle in c# because it will fire and forget but Listener to handle the response.
public async Task<List<InAppBillingProduct>> GetProductsAsync(List<string> ProductIds)
{
var getSkuDetailsTask = Task.Factory.StartNew(() =>
{
var prms = SkuDetailsParams.NewBuilder();
var type = BillingClient.SkuType.Inapp;
prms.SetSkusList(ProductIds).SetType(type);
BillingClient.QuerySkuDetailsAsync(prms.Build(), new SkuDetailsResponseListener());
return InAppBillingProducts;
});
return await getSkuDetailsTask;
}
2nd question regarding how to handle with the listener as below. How do I return value from the listener. I need return list of InAppBillingProduct object.
public class SkuDetailsResponseListener : Java.Lang.Object, ISkuDetailsResponseListener
{
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skus)
{
if (billingResult.ResponseCode == BillingResponseCode.Ok)
{
// get list of Products here and return
}
}
}
FYI. This is how I did it. This is not a complete code but this will give you and idea.
Listener - PCL
============
private async Task EventClicked()
{
var skuList = new List<string>();
skuList.Add("[nameofsubscriptionfoundinyourgoogleplay]");
if (await _billingClientLifecycle.Initialize(skuList, DisconnectedConnection))
{
var firstProduct = _billingClientLifecycle?.ProductsInStore?.FirstOrDefault();
if (firstProduct != null)
{
//purchase here
}
}
}
private void DisconnectedConnection()
{
//Todo.alfon. handle disconnection here...
}
Interface - PCL
===========
public interface IInAppBillingMigratedNew
{
List<InAppBillingPurchase> PurchasedProducts { get; set; }
List<InAppBillingProduct> ProductsInStore { get; set; }
Task<bool> Initialize(List<String> skuList, Action onDisconnected = null);
}
Dependency - Platform Droid
===============
[assembly: XF.Dependency(typeof(InAppBillingMigratedNew))]
public class InAppBillingMigratedNew : Java.Lang.Object, IBillingClientStateListener
, ISkuDetailsResponseListener, IInAppBillingMigratedNew
{
private Activity Context => CrossCurrentActivity.Current.Activity
?? throw new NullReferenceException("Current Context/Activity is null");
private BillingClient _billingClient;
private List<string> _skuList = new List<string>();
private TaskCompletionSource<bool> _tcsInitialized;
private Action _disconnectedAction;
private Dictionary<string, SkuDetails> _skusWithSkuDetails = new Dictionary<string, SkuDetails>();
public List<InAppBillingPurchase> PurchasedProducts { get; set; }
public List<InAppBillingProduct> ProductsInStore { get; set; }
public IntPtr Handle => throw new NotImplementedException();
public Task<bool> Initialize(List<string> skuList, Action disconnectedAction = null)
{
_disconnectedAction = disconnectedAction;
_tcsInitialized = new TaskCompletionSource<bool>();
var taskInit = _tcsInitialized.Task;
_skuList = skuList;
_billingClient = BillingClient.NewBuilder(Context)
.SetListener(this)
.EnablePendingPurchases()
.Build();
if (!_billingClient.IsReady)
{
_billingClient.StartConnection(this);
}
return taskInit;
}
#region IBillingClientStateListener
public void OnBillingServiceDisconnected()
{
Console.WriteLine($"Connection disconnected.");
_tcsInitialized?.TrySetResult(false);
_disconnectedAction?.Invoke();
}
public void OnBillingSetupFinished(BillingResult billingResult)
{
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
if (responseCode == BillingResponseCode.Ok)
{
QuerySkuDetails();
QueryPurchases();
_tcsInitialized?.TrySetResult(true);
}
else
{
Console.WriteLine($"Failed connection {debugMessage}");
_tcsInitialized?.TrySetResult(false);
}
}
#endregion
#region ISkuDetailsResponseListener
public void OnSkuDetailsResponse(BillingResult billingResult, IList<SkuDetails> skuDetailsList)
{
if (billingResult == null)
{
Console.WriteLine("onSkuDetailsResponse: null BillingResult");
return;
}
var responseCode = billingResult.ResponseCode;
var debugMessage = billingResult.DebugMessage;
switch (responseCode)
{
case BillingResponseCode.Ok:
if (skuDetailsList == null)
{
_skusWithSkuDetails.Clear();
}
else
{
if (skuDetailsList.Count > 0)
{
ProductsInStore = new List<InAppBillingProduct>();
}
foreach (var skuDetails in skuDetailsList)
{
_skusWithSkuDetails.Add(skuDetails.Sku, skuDetails);
//ToDo.alfon. make use mapper here
ProductsInStore.Add(new InAppBillingProduct
{
Name = skuDetails.Title,
Description = skuDetails.Description,
ProductId = skuDetails.Sku,
CurrencyCode = skuDetails.PriceCurrencyCode,
LocalizedIntroductoryPrice = skuDetails.IntroductoryPrice,
LocalizedPrice = skuDetails.Price,
MicrosIntroductoryPrice = skuDetails.IntroductoryPriceAmountMicros,
MicrosPrice = skuDetails.PriceAmountMicros
});
}
}
break;
case BillingResponseCode.ServiceDisconnected:
case BillingResponseCode.ServiceUnavailable:
case BillingResponseCode.BillingUnavailable:
case BillingResponseCode.ItemUnavailable:
case BillingResponseCode.DeveloperError:
case BillingResponseCode.Error:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
case BillingResponseCode.UserCancelled:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
// These response codes are not expected.
case BillingResponseCode.FeatureNotSupported:
case BillingResponseCode.ItemAlreadyOwned:
case BillingResponseCode.ItemNotOwned:
default:
Console.WriteLine("onSkuDetailsResponse: " + responseCode + " " + debugMessage);
break;
}
}
#endregion
#region Helper Methods Private
private void ProcessPurchases(List<Purchase> purchasesList)
{
if (purchasesList == null)
{
Console.WriteLine("No purchases done.");
return;
}
if (IsUnchangedPurchaseList(purchasesList))
{
Console.WriteLine("Purchases has not changed.");
return;
}
_purchases.AddRange(purchasesList);
PurchasedProducts = _purchases.Select(sku => new InAppBillingPurchase
{
PurchaseToken = sku.PurchaseToken
})?.ToList();
if (purchasesList != null)
{
LogAcknowledgementStatus(purchasesList);
}
}
private bool IsUnchangedPurchaseList(List<Purchase> purchasesList)
{
// TODO: Optimize to avoid updates with identical data.
return false;
}
private void LogAcknowledgementStatus(List<Purchase> purchasesList)
{
int ack_yes = 0;
int ack_no = 0;
foreach (var purchase in purchasesList)
{
if (purchase.IsAcknowledged)
{
ack_yes++;
}
else
{
ack_no++;
}
}
//Log.d(TAG, "logAcknowledgementStatus: acknowledged=" + ack_yes +
// " unacknowledged=" + ack_no);
}
private void QuerySkuDetails()
{
var parameters = SkuDetailsParams
.NewBuilder()
.SetType(BillingClient.SkuType.Subs)
.SetSkusList(_skuList)
.Build();
_billingClient.QuerySkuDetailsAsync(parameters, this);
}
private void QueryPurchases()
{
if (!_billingClient.IsReady)
{
Console.WriteLine("queryPurchases: BillingClient is not ready");
}
var result = _billingClient.QueryPurchases(BillingClient.SkuType.Subs);
ProcessPurchases(result?.PurchasesList?.ToList());
}
#endregion
}

Click an Item -hold- and swipe to an other site

If an object is clicked, the next page should not be called immediately. But the click should remain on the object until you scroll through a wipe to the next page.
How can it hold the click command on an Item?
How can it swipe from the clicked Item to an other Page?
Update
Click one item > OnHold> swipe from the holded item to the left and right.
This is the actual behavior:
private int index = -1;
break;
}
return true;
}
}
To highlight the item when it is clicked, you can set background color to the item's view, to perform a swipe gesture for each item, I think you will need to implement IOnTouchListener for each item. Here I created an adapter to implement this feature:
public class LVAdapter : BaseAdapter<ListItemModel>, View.IOnTouchListener
{
private List<ListItemModel> items = new List<ListItemModel>();
private Activity context;
private int index = -1;
public enum SwipeAction
{
LR, // Left to Right
RL, // Right to Left
TB, // Top to bottom
BT, // Bottom to Top
None // when no action was detected
}
private int MIN_DISTANCE = 100;
private float downX, downY, upX, upY;
private SwipeAction maction = SwipeAction.None;
public LVAdapter(Activity context, List<ListItemModel> items) : base()
{
this.context = context;
this.items = items;
}
public override ListItemModel this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override long GetItemId(int position)
{
return position;
}
private void SetSelectedItem(int position)
{
index = position;
NotifyDataSetChanged();
}
private class MyViewHolder : Java.Lang.Object
{
public TextView Name { get; set; }
public TextView Description { get; set; }
public int index { get; set; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
MyViewHolder holder = null;
var view = convertView;
if (view != null)
holder = view.Tag as MyViewHolder;
if (holder == null)
{
holder = new MyViewHolder();
view = context.LayoutInflater.Inflate(Resource.Layout.ItemCell, null);
holder.Name = view.FindViewById<TextView>(Resource.Id.nametxt);
holder.Description = view.FindViewById<TextView>(Resource.Id.detailtxt);
holder.index = position;
view.Tag = holder;
}
holder.Name.Text = items[position].Name;
holder.Description.Text = items[position].Description;
if (index != -1 && position == index)
{
holder.Name.SetBackgroundColor(Android.Graphics.Color.Red);
holder.Description.SetBackgroundColor(Android.Graphics.Color.Pink);
}
else
{
holder.Name.SetBackgroundColor(Android.Graphics.Color.RoyalBlue);
holder.Description.SetBackgroundColor(Android.Graphics.Color.SeaGreen);
}
view.SetOnTouchListener(this);
return view;
}
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
downX = e.GetX();
downY = e.GetY();
maction = SwipeAction.None;
break;
case MotionEventActions.Move:
upX = e.GetX();
upY = e.GetY();
var deltaX = downX - upX;
var deltaY = downY - upY;
if (Math.Abs(deltaX) > MIN_DISTANCE)
{
if (deltaX < 0)
{
maction = SwipeAction.LR;
}
else if (deltaX > 0)
{
maction = SwipeAction.RL;
}
return true;
}
else if (Math.Abs(deltaY) > MIN_DISTANCE)
{
if (deltaY < 0)
{
maction = SwipeAction.TB;
}
else if (deltaY > 0)
{
maction = SwipeAction.BT;
}
return false;
}
break;
case MotionEventActions.Up:
var holder = v.Tag as MyViewHolder;
if (maction == SwipeAction.None)
{
SetSelectedItem(holder.index);
}
else if (maction == SwipeAction.LR | maction == SwipeAction.RL)
{
if (holder.index == index)
context.StartActivity(typeof(Activity1));
}
break;
}
return true;
}
}
The ListItemModel is quite simple by my side:
public class ListItemModel
{
public string Name { get; set; }
public string Description { get; set; }
}
You can try to modify the model and holder as you need.

Is there a Virtualization for gridview in windows store app?

Problem is faced, when we come across huge data items, where while scrolling the cells become black and glitches are seen throughout....any suggestion are appreciated.
Why not try IncrementalLoading to load your data in gridview.
Tutorial
Incase the link stops working
Code Snippet
public interface IIncrementalSource<T>
{
Task<IEnumerable<T>> GetPagedItems(int pageIndex, int pageSize);
}
public class IncrementalLoadingCollection<T, I> : ObservableCollection<I>,
ISupportIncrementalLoading
where T : IIncrementalSource<I>, new()
{
private T source;
private int itemsPerPage;
private bool hasMoreItems;
private int currentPage;
public IncrementalLoadingCollection(int itemsPerPage = 20)
{
this.source = new T();
this.itemsPerPage = itemsPerPage;
this.hasMoreItems = true;
}
public bool HasMoreItems
{
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () =>
{
uint resultCount = 0;
var result = await source.GetPagedItems(currentPage++, itemsPerPage);
if (result == null || result.Count() == 0)
{
hasMoreItems = false;
}
else
{
resultCount = (uint)result.Count();
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() =>
{
foreach (I item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}

Accessing an extension method on a dbset subtype

I have an extension method defined as:
public static class CurrentItemExtensions
{
static GPOPricingEntities ctx = new GPOPricingEntities();
public static List<CurrentItem> Get(this DbSet<CurrentItem> item, int tierId, string contractId)
{
List<CurrentItem> items = ctx.Items.OfType<CurrentItem>().Where(x => x.TierId == tierId).ToList();
if (items == null)
{
GPOPricing.AS400Models.ItemCollection collection = new GPOPricing.AS400Models.ItemCollection().Get(contractId);
foreach (var c in collection)
{
CurrentItem target = new CurrentItem();
target.Price = c.DirectPriceEaches;
target.SKU = c.LongItemNbr;
target.Description = c.Description;
target.ProductLine = c.ProductLine;
items.Add(target);
}
}
else
{
foreach (var i in items)
{
GPOPricing.AS400Models.Item as400Item = new GPOPricing.AS400Models.ItemCollection().GetBySKU(i.SKU);
i.Description = as400Item.Description;
i.ProductLine = as400Item.ProductLine;
}
}
return items;
}
}
The problem I'm having is accessing it - CurrentItem is a subtype of Item. So I've tried:
db.Items.Get (doesn't work)
and I have tried
db.Items.OfType<CurrentItem>().Get (doesn't work)
Any suggestions?
I found that I had to use the base type and create a method for each subtype:
public static class CurrentItemExtensions
{
static GPOPricingEntities ctx = new GPOPricingEntities();
public static List<CurrentItem> GetCurrentItems(this DbSet<Item> item, int tierId, string contractId)
{
List<CurrentItem> items = ctx.Items.OfType<CurrentItem>().Where(x => x.TierId == tierId).ToList();
if (items.Count() == 0)
{
GPOPricing.AS400Models.ItemCollection collection = new GPOPricing.AS400Models.ItemCollection().Get(contractId);
foreach (var c in collection)
{
CurrentItem target = new CurrentItem();
target.Price = c.DirectPriceEaches;
target.SKU = c.LongItemNbr;
items.Add(target);
}
}
else
{
foreach (var i in items)
{
GPOPricing.AS400Models.Item as400Item = new GPOPricing.AS400Models.ItemCollection().GetBySKU(i.SKU);
}
}
return items;
}
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Resources