Layout render error when using Tasks modifying controls - android

Description
Hello,
Rendering of Layouts are not updated if they contains controls (ContentView) with some rendering Task !
I created a simple ContentView which contains three Image (the FireControl in the screenshot).
In the constructor, I use a simple async method, with Task.Factory.StartNew, which will play on the visibility of the Face images with a small Task.Delay between them.
In the MainPage, I load this control in a StackLayout, using a button : "Load control".
It is impossible for me to add a second control (if I press again "Load control" )!
It's like if the rendering of the StackLayout in my MainPage was not updated ...
If I dont call my task method: no rendering problem!
If I dont touch controls in my task method: no rendering problem !
If I spam-click "Load control" and "Remove control", sometimes loaded controls appears...
Is there a way to tell my StackLayout to update its rendering after adding a control?
Is there a better way than Task.Factory.StartNew to perform frame animation in a control, so as not to block the rendering?
(In this example the desired animation has been simplified)
Steps to Reproduce
My Test solution :
Test Solution
TestControl
public class TestControl : ContentView
{
protected Image FaceNormal;
protected Image FaceLoose;
public TestControl()
{
var principalImage = new Image { Source = "fire_principal.png" }; // The body
FaceNormal = new Image { Source = "fire_facenormal.png" }; // Opened eyes
FaceLoose = new Image { Source = "fire_faceloose.png" }; // Closed eyes
Content = new Grid { Children = { principalImage, FaceNormal, FaceLoose } }; // Loaded in the Content
// /!\ Causes rendering errors in the StackLayout
Task.Factory.StartNew(() => StartFaceAnimation());
}
public async Task StartFaceAnimation()
{
Dispatcher.Dispatch(() =>
{
FaceNormal.IsVisible = true;
FaceLoose.IsVisible = false;
});
await Task.Delay(2000);
Dispatcher.Dispatch(() =>
{
FaceNormal.IsVisible = false;
FaceLoose.IsVisible = true;
});
}
}
MainPage
public class MainPage : ContentPage
{
public MainPage()
{
var verticalStackLayout = new VerticalStackLayout();
var loadTestControlButton = new Button { Text = "Load control", Margin = 2 };
loadTestControlButton.Clicked += (o, e) => verticalStackLayout.Children.Add(new TestControl());
verticalStackLayout.Children.Add(loadTestControlButton);
var removeTestControlButton = new Button { Text = "Remove control", Margin = 2 };
removeTestControlButton.Clicked += (o, e) => verticalStackLayout.Children.Remove(verticalStackLayout.Children.Last());
verticalStackLayout.Children.Add(removeTestControlButton);
Content = verticalStackLayout;
}
}

The trick is to call Arrange after IsVisible, see the below modifications:
public async Task StartFaceAnimation()
{
Dispatcher.Dispatch(() =>
{
FaceNormal.IsVisible = true;
FaceLoose.IsVisible = false;
Arrange(new Rect());
});
await Task.Delay(2000);
Dispatcher.Dispatch(() =>
{
FaceNormal.IsVisible = false;
FaceLoose.IsVisible = true;
Arrange(new Rect());
});
}
It redraws the controls!

Related

Unity GameObject "flash" after SetActive on Android Project

I have a project that use Android to display Unity Player. So I export Untiy project as Android module which implemented by Android Application.
I create buttons in Android Activity which contains UnityPlayer, And when I click button, it send a message to Unity Player to invoke C# function, just like this:
findViewById(R.id.btnChange).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mUnityPlayer.UnitySendMessage("ScriptHolder", "ChangeSkin", "");
}
});
And the function named "ChangeSkin" is just to change some GameObjects' active. Just like this:
void ChangeSkin()
{
int prefab;
if (_currentPrefab == PREFAB_DEFAULT)
{
prefab = PREFAB_PRINCESS;
}
else
{
prefab = PREFAB_DEFAULT;
}
ShowSkin(prefab);
}
private void ShowSkin(int prefab)
{
_currentPrefab = prefab;
foreach (var item in _defaultDressList)
{
item.SetActive(prefab == PREFAB_DEFAULT);
}
foreach (var item in _princessDressList)
{
item.SetActive(prefab == PREFAB_PRINCESS);
}
}
And something weird happening: when I click button to change the person's cloth in Unity, the GameObjects which called SetActive(true) show at the position above the right position for a frame and become normal, it looks like they flash. Here is the gif of the project demo:
It looks like the position offset is equal to the height of status bar. If I create a button on Unity Scene and call "ChangeSkin" function, everything will be OK.
I tried all I can to fix this but not succeed. So I hope you will help me, thx.
You should check your Unity layout. If you have a layout group that has content size fitter component, and the child objects also have it (content size fitter), this could cause these glitches.
I fixed this problem by using a flag (or trigger). Just like this:
// set value true to trigger ChangeSkin() in Update(),
// instead of invoke ChangeSkin() directly
private bool _changingSkinTrigger = false;
void ChangeSkin()
{
if (_currentPrefab == PREFAB_DEFAULT)
{
_currentPrefab = PREFAB_PRINCESS;
}
else
{
_currentPrefab = PREFAB_DEFAULT;
}
_changingSkinTrigger = true;
}
void Update()
{
if (_changingSkinTrigger)
{
_changingSkinTrigger = false;
ShowSkin();
}
}
private void ShowSkin()
{
foreach (var item in _defaultDressList)
{
item.SetActive(_currentPrefab == PREFAB_DEFAULT);
}
foreach (var item in _princessDressList)
{
item.SetActive(_currentPrefab == PREFAB_PRINCESS);
}
}
Thank #AndriyMarshalek for enlightening me.

Handle Talkback in a Xamarin app using a virtual DPAD

I have a Xamarin app that was not meant to handle the talkback functionality of android, because for it to work well it had to be build in a specific way.
My app is a little order, and I simply can't make a do-over of the whole thing.
So, what is happening?
My Xamarin app is made with non-native libs, that are not supported by the Talkback, so, when the user turns on the Talkback functionality the app effectively stops receiving the DPAD events since they are handled by the systems Accessibility Service.
That service, gets the events, and tries to handle them within my app, but, since my components are non-native, the system does not recognize them and the DPAD is wasted, hence, the illusion that the DPADs are not working.
So, what do you have to do if you just want to handle the DPADs (and nothing else) yourself with Talkback on?
The answer to this post will contain the code that describes the following behavior:
1. The talkback wont be able to 'talk' about your components
2. The DPAD events will be handled by an Accessibility Delegate
3. A virtual DPAD will handle the navigation
4. The green rectangle used for focus will be disabled, since you wont need it anyway
5. The app will look exactly the same with Talkback on and off
This post was made for educational purposes, since I had a hard time coming up with the solution, and hope the next guy finds it helpfull.
The first step is to create a class that inherits the AccessibilityDelegateCompat in order to create our own Accessibility Service.
class MyAccessibilityHelper : AccessibilityDelegateCompat
{
const string Tag = "MyAccessibilityHelper";
const int ROOT_NODE = -1;
const int INVALID_NODE = -1000;
const string NODE_CLASS_NAME = "My_Node";
public const int NODE_UP = 1;
public const int NODE_LEFT = 2;
public const int NODE_CENTER = 3;
public const int NODE_RIGHT = 4;
public const int NODE_DOWN = 5;
private class MyAccessibilityProvider : AccessibilityNodeProviderCompat
{
private readonly MyAccessibilityHelper mHelper;
public MyAccessibilityProvider(MyAccessibilityHelper helper)
{
mHelper = helper;
}
public override bool PerformAction(int virtualViewId, int action, Bundle arguments)
{
return mHelper.PerformNodeAction(virtualViewId, action, arguments);
}
public override AccessibilityNodeInfoCompat CreateAccessibilityNodeInfo(int virtualViewId)
{
var node = mHelper.CreateNode(virtualViewId);
return AccessibilityNodeInfoCompat.Obtain(node);
}
}
private readonly View mView;
private readonly MyAccessibilityProvider mProvider;
private Dictionary<int, Rect> mRects = new Dictionary<int, Rect>();
private int mAccessibilityFocusIndex = INVALID_NODE;
public MyAccessibilityHelper(View view)
{
mView = view;
mProvider = new MyAccessibilityProvider(this);
}
public override AccessibilityNodeProviderCompat GetAccessibilityNodeProvider(View host)
{
return mProvider;
}
public override void SendAccessibilityEvent(View host, int eventType)
{
Android.Util.Log.Debug(Tag, "SendAccessibilityEvent: host={0} eventType={1}", host, eventType);
base.SendAccessibilityEvent(host, eventType);
}
public void AddRect(int id, Rect rect)
{
mRects.Add(id, rect);
}
public AccessibilityNodeInfoCompat CreateNode(int virtualViewId)
{
var node = AccessibilityNodeInfoCompat.Obtain(mView);
if (virtualViewId == ROOT_NODE)
{
node.ContentDescription = "Root node";
ViewCompat.OnInitializeAccessibilityNodeInfo(mView, node);
foreach (var r in mRects)
{
node.AddChild(mView, r.Key);
}
}
else
{
node.ContentDescription = "";
node.ClassName = NODE_CLASS_NAME;
node.Enabled = true;
node.Focusable = true;
var r = mRects[virtualViewId];
node.SetBoundsInParent(r);
int[] offset = new int[2];
mView.GetLocationOnScreen(offset);
node.SetBoundsInScreen(new Rect(offset[0] + r.Left, offset[1] + r.Top, offset[0] + r.Right, offset[1] + r.Bottom));
node.PackageName = mView.Context.PackageName;
node.SetSource(mView, virtualViewId);
node.SetParent(mView);
node.VisibleToUser = true;
if (virtualViewId == mAccessibilityFocusIndex)
{
node.AccessibilityFocused = true;
node.AddAction(AccessibilityNodeInfoCompat.ActionClearAccessibilityFocus);
}
else
{
node.AccessibilityFocused = false;
node.AddAction(AccessibilityNodeInfoCompat.FocusAccessibility);
}
}
return node;
}
private AccessibilityEvent CreateEvent(int virtualViewId, EventTypes eventType)
{
var e = AccessibilityEvent.Obtain(eventType);
if (virtualViewId == ROOT_NODE)
{
ViewCompat.OnInitializeAccessibilityEvent(mView, e);
}
else
{
var record = AccessibilityEventCompat.AsRecord(e);
record.Enabled = true;
record.SetSource(mView, virtualViewId);
record.ClassName = NODE_CLASS_NAME;
e.PackageName = mView.Context.PackageName;
}
return e;
}
public bool SendEventForVirtualView(int virtualViewId, EventTypes eventType)
{
if (mView.Parent == null)
return false;
var e = CreateEvent(virtualViewId, eventType);
return ViewParentCompat.RequestSendAccessibilityEvent(mView.Parent, mView, e);
}
public bool PerformNodeAction(int virtualViewId, int action, Bundle arguments)
{
if (virtualViewId == ROOT_NODE)
{
return ViewCompat.PerformAccessibilityAction(mView, action, arguments);
}
else
{
switch (action)
{
case AccessibilityNodeInfoCompat.ActionAccessibilityFocus:
if (virtualViewId != mAccessibilityFocusIndex)
{
if (mAccessibilityFocusIndex != INVALID_NODE)
{
SendEventForVirtualView(mAccessibilityFocusIndex, EventTypes.ViewAccessibilityFocusCleared);
}
mAccessibilityFocusIndex = virtualViewId;
mView.Invalidate();
SendEventForVirtualView(virtualViewId, EventTypes.ViewAccessibilityFocused);
// virtual key event
switch (virtualViewId)
{
case NODE_UP:
HandleDpadEvent(Keycode.DpadUp);
break;
case NODE_LEFT:
HandleDpadEvent(Keycode.DpadLeft);
break;
case NODE_RIGHT:
HandleDpadEvent(Keycode.DpadRight);
break;
case NODE_DOWN:
HandleDpadEvent(Keycode.DpadDown);
break;
}
// refocus center
SendEventForVirtualView(NODE_CENTER, EventTypes.ViewAccessibilityFocused);
return true;
}
break;
case AccessibilityNodeInfoCompat.ActionClearAccessibilityFocus:
mView.RequestFocus();
if (virtualViewId == mAccessibilityFocusIndex)
{
mAccessibilityFocusIndex = INVALID_NODE;
mView.Invalidate();
SendEventForVirtualView(virtualViewId, EventTypes.ViewAccessibilityFocusCleared);
return true;
}
break;
}
}
return false;
}
private void HandleDpadEvent(Keycode keycode)
{
//Here you know what DPAD was pressed
//You can create your own key event and send it to your app
//This code depends on your own application, and I wont be providing the code
//Note, it is important to handle both, the KeyDOWN and the KeyUP event for it to work
}
}
Since the code is a bit large, I'll just explain the crutal parts.
Once the talkback is active, the dictionary (from our view bellow) will be used to create a virtual tree node of our virtual DPAD. With that in mind, the function PerformNodeAction will be the most important one.
It handles the actions once a virtual node was focused by the Accessibility system, based on the provided id of the virtual element, there are two parts, the first one is the ROOT_NODE, which is the view iteslf that contains our virtual dpad, which for the most part can be ignored, but the seond part is where the handling is done.
The second part is where the actions ActionAccessibilityFocus and ActionClearAccessibilityFocus are handled. The two of witch are both important, but the first one is where we can finally handle our virtual dpad.
What is done here is that with the provided virtual ID from the dictionary, we know which DPAD was selected (virtualViewId). Based on the selected DPAD, we can perform the action we want in the HandleDpadEvent function. What is important to notice, is that after we handle the selecteds DPAD event, we will refocus our CENTER node, in order to be ready to handle the next button press. This is very important, since, you dont want to find yourself in a situation where you go DOWN, and then UP, just for the virtual dpad to focus the CENTER pad.
So, I'll epeat myself, the refocusing of the CENTER pad after the previous' DPAD event was handled needs to be done in order for us to know EXACTLY where we will be after the next DPAD button was pressed!
There is one function that I wont post here, since the code for it is very specific for my app, the function is HandleDpadEvent, there you must create a keydown and a keyup event and send it to your main activity where the function onKeyDown/Up will be triggered. Once you do that, the delegate is done.
And once the Delegate is done, we have to make our view like this:
/**
* SimplestCustomView
*/
public class AccessibilityHelperView : View
{
private MyAccessibilityHelper mHelper;
Dictionary<int, Rect> virtualIdRectMap = new Dictionary<int, Rect>();
public AccessibilityHelperView(Context context) :
base(context)
{
Init();
}
public AccessibilityHelperView(Context context, IAttributeSet attrs) :
base(context, attrs)
{
Init();
}
public AccessibilityHelperView(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
Init();
}
public void Init()
{
this.SetFocusable(ViewFocusability.Focusable);
this.Focusable = true;
this.FocusedByDefault = true;
setRectangle();
mHelper = new MyAccessibilityHelper(this);
ViewCompat.SetAccessibilityDelegate(this, mHelper);
foreach (var r in virtualIdRectMap)
{
mHelper.AddRect(r.Key, r.Value);
}
}
private void setRectangle()
{
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_CENTER, new Rect(1, 1, 2, 2));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_LEFT, new Rect(0, 1, 1, 2));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_UP, new Rect(1, 0, 2, 1));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_RIGHT, new Rect(2, 1, 3, 2));
virtualIdRectMap.Add(MRAccessibilityHelper.NODE_DOWN, new Rect(1, 2, 2, 3));
}
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
}
}
That view looks like this:
What is to notice?
The size of the node pads is in pixels, and they will be found on the top left corner of your app.
They are set to that single pixel size, because the Talkback functionality would otherwise select the first node pad that was added to the dictionary with a green rectangle (thats standard behavior for talkback)
All the rectangles in the view are added to a dictionary that will be used in our own Accessibility Delegate, to mention here is that the CENTER pad was added first, and therefor will be in focus once the talkback is activated by default
The Init function
The Init function is crutial for this, there we will create our view, and set some talkback parameters nessessary for our virtual dpad to be recognized by the systems own Accessibility Service.
Also, there will our Accessibility Delegate be initialized and our dictionary with all the created DPADs.
Ok, so far, we made a Delegate and a View, I placed them both in the same file, so they can see each other. But it is not a must.
So what now? We must add the AccessibilityHelperView to our app, in the MainActivity.cs file
AccessibilityHelperView mAccessibilityHelperView;
In the OnCreate function, you can add the following code to initiate the view:
mAccessibilityHelperView = new AccessibilityHelperView(this);
In the OnResume function, you can check if the talkback is on or off, based on the result, you can add or remove the mAccessibilityHelperView from your mBackgroundLayout(AddView, and RemoveView).
The OnResume function should look like this:
if (TalkbackEnabled && !_isVirtualDPadShown)
{
mBackgroundLayout.AddView(mAccessibilityHelperView);
_isVirtualDPadShown = true;
}
else if (!TalkbackEnabled && _isVirtualDPadShown)
{
mBackgroundLayout.RemoveView(mAccessibilityHelperView);
_isVirtualDPadShown = false;
}
The TalkbackEnabled variable is a local one that checks if the Talkback service is on or off, like this:
public bool TalkbackEnabled
{
get
{
AccessibilityManager am = MyApp.Instance.GetSystemService(Context.AccessibilityService) as AccessibilityManager;
if (am == null) return false;
String TALKBACK_SETTING_ACTIVITY_NAME = "com.android.talkback.TalkBackPreferencesActivity";
var serviceList = am.GetEnabledAccessibilityServiceList(FeedbackFlags.AllMask);
foreach (AccessibilityServiceInfo serviceInfo in serviceList)
{
String name = serviceInfo.SettingsActivityName;
if (name.Equals(TALKBACK_SETTING_ACTIVITY_NAME))
{
Log.Debug(LogArea, "Talkback is active");
return true;
}
}
Log.Debug(LogArea, "Talkback is inactive");
return false;
}
}
That should be all you need to make it work.
Hope I could help you out.

Navigating between Multiple pages in Xamarin.forms?

I have a content view and left navigation drawer which are commons on 4 pages and Content view contains 4 icons which navigates between 4 different pages. I want to keep my content view and left navigation drawer on all 4 pages.
I create a Master details page and set Master page to Left navigation Drawer and i am changing the details page each time.
I got the exception as android can navigate only page at a time while navigating between multiple pages.
Following is my Rootpage and ContentView Page
public class RootPage : MasterDetailPage
{
LeftNavigationPanel menuPage;
public RootPage(string detailSel)
{
menuPage = new LeftNavigationPanel(); //This is the left navigation class. rename later.
Master = menuPage;
if (detailSel.Equals(""))
{
var detail = new NavigationPage(new Tabpage());
Detail = detail; //homepage
detail.Icon = "leftnav.png";
App.navigation = detail.Navigation;
}
else if (detailSel.Equals("1"))
{
var detail = new NavigationPage(new Post());
Detail = detail; //homepage
detail.Icon = "leftnav.png";
App.navigation = detail.Navigation;
// System.Diagnostics.Debug.WriteLine("page1: " + Navigation.NavigationStack[Navigation.NavigationStack.Count-1]);
}
else if (detailSel.Equals("2"))
{
var detail = new NavigationPage(new TrackTabPage());
Detail = detail; //homepage
detail.Icon = "leftnav.png";
App.navigation = detail.Navigation;
// System.Diagnostics.Debug.WriteLine("page1: " + Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
}
}
}
ContentView
public partial class HomeContentView : ContentView
{
public HomeContentView()
{
InitializeComponent();
}
private async void read_click(Object sender, EventArgs e)
{
if (!((Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]) is Tabpage))
{
//await Navigation.PopAsync();
await Navigation.PushAsync(new RootPage(""));
// this.Navigation.PopAsync();
}
}
private async void post_click(Object sender, EventArgs e)
{
if (!((Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]) is Post))
{
System.Diagnostics.Debug.WriteLine("page: "+ Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
// await Navigation.PopAsync();
await Navigation.PushAsync(new RootPage("1"));
}
}
private async void track_click(Object sender, EventArgs e)
{
if (!((Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]) is TrackTabPage))
{
System.Diagnostics.Debug.WriteLine("page: " + Navigation.NavigationStack[Navigation.NavigationStack.Count - 1]);
// await Navigation.PopAsync();
await Navigation.PushAsync(new RootPage("2"));
}
}
private void play_click(Object sender, EventArgs e) { }
}
You should use the Navigation from the Detail-Page, because you set the Detail as a new NavigationPage().
I usually use following code to geht the INavigation to navigate inside my app:
public static INavigation GetNavigation()
{
var mdp = Application.Current.MainPage as MasterDetailPage;
if (mdp != null)
{
// Return the navigation of the detail-page
return mdp.Detail.Navigation;
}
var np = Application.Current.MainPage as NavigationPage;
if (np != null)
{
// Page is no MasterDetail-Page, but has a navigation
return np.Navigation;
}
// No navigation found (just set the page, instead of navigation)
return null;
}
And use it like the following example:
var nav = GetNavigation();
if(nav != null)
{
await nav.PushAsync(new Tabpage());
}
else
{
// Just set the page, because there is no navigation
Application.Current.MainPage = new new Tabpage();
// Or like this
Application.Current.MainPage = new NavigationPage(new Tabpage());
}
Update
I changed my answer a little bit (thanks for the comment, I just don't realized this). Do not use new Rootpage("") to change the page. With that you create a new Navigation on top of the current Navigation.. that makes no sense.
Just Push your page onto the current navigation you get by GetNavigation().
Please read the documentation about the navigation pattern in Xamarin.Forms for further informations.

How to make an async button click update a TextView dynamically with Xamarin Android

In Xamarin Native Android app I am calling methodOne() and getting some details. After this I want to updated the details to text view and then calls the methodTwo(). After executing MethodTwo I have to clear the text details from text view. I tried with
RunOnUiThread(() => tColorDetail.SetText("text", TextView.BufferType.Normal));
The details are displaying after finishing all the methods it is not showing immediately after executing MethodOne().
What is missing here?
.....................................................
public class MainActivity : Activity
{
int count = 1;
ticket Ticket;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our button from the layout resource,
// and attach an event to it
Button bClick= FindViewById<Button>(Resource.Id.bSub);
try {
bClick.Click += delegate {
Job Job;
Action act = new Action();
EditText Address = (EditText)FindViewById(Resource.Id.Address);
act.Address = Address.Text.ToString();
if (!ValidateIPv4(act.Address))
{
ShowMessage("Invalid IP");
}
else
{//TextView Mode = (TextView)FindViewById(Resource.Id.Mode);
string httpUrl = act.iPAddress ;
Ticket = act.ExecuteTicket(httpUrl, "Ticket");
//RunOnUiThread(() => tColorMode.SetText("text", TextView.BufferType.Normal));
// this.RunOnUiThread(() => setTicket(true));
setStatus(" Ticket : Success", true);
setTicket(true);
//tColorMode.SetText("text",TextView.BufferType.Normal);
//tColorMode.Invalidate();
Job = act.executeJob(httpUrl, Ticket);
setStatus(" Job : Success", true);
act.File(httpUrl, Ticket, Job);
setStatus("File Retrieval : Success", true);
setTicket(false);
setStatus("File Retrieval : Success", false);
}
};
}
catch (Exception ex)
{
ShowMessage(ex.Message.ToString());
}
}
public bool ValidateIPv4(string ipString)
{
if (String.IsNullOrWhiteSpace(ipString))
{
return false;
}
string[] splitValues = ipString.Split('.');
if (splitValues.Length != 4)
{
return false;
}
return true;
}
public void setTicket(bool b)
{
//TextView tMode = (TextView)FindViewById(Resource.Id.tMode);
// tMode.SetText("text",TextView.BufferType.Normal);
RunOnUiThread(() => {
TextView tMode = (TextView)FindViewById(Resource.Id.Mode);
tColorMode.SetText("text",TextView.BufferType.Normal);// b==true?Ticket.getProcessing():"";
((TextView)this.FindViewById(Resource.Id.tRes)).Text = b == true ? Ticket.getHeight().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tFormat)).Text = b == true ? Ticket.getFormat().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tOrode)).Text = b == true ? Ticket.getType().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tRot)).Text = b == true ? Ticket.getValue().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tSource)).Text = b == true ? Ticket.getSource().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tSize)).Text = b == true ? Ticket.getAutoDetect().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tExpo)).Text = b == true ? Ticket.getAuto().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tSharp)).Text = b == true ? Ticket.getSharp().ToString() : "";
((TextView)this.FindViewById(Resource.Id.tComp)).Text = b == true ? Ticket.getComp().ToString() : "";
//tColorMode.Invalidate();
}); }
private void ShowMessage(string msg)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog dialog = builder.Create();
dialog.SetMessage(msg);
dialog.SetButton("OK", (s, ev) =>
{
dialog.Cancel();
});
dialog.Show();
}
private void setStatus(string status,bool b)
{
((TextView)this.FindViewById(Resource.Id.tStatus)).Text = b == true ? status : " ";
}
}
Without access to the full code it is tricky to give you an answer but
Ticket = act.ExecuteTicket(httpUrl, "Ticket");
this line has the potential to take a while to complete if it performs network operations and probably runs in a background thread not to freeze the UI.
Presumably when you get to
// this.RunOnUiThread(() => setTicket(true));setStatus(" Ticket : Success", true);
setTicket(true);
the act.ExecuteTicket call might not have finished
also if there are async operations taking place in
Job = act.executeJob(httpUrl, Ticket);
it could take a while to complete
All taken into consideration, you could probably be getting to
setTicket(false);
setStatus("File Retrieval : Success", false);
before the first calls finish
It could be worth checking
ANSWER UPDATE after discussion in comments:
Since your problem is that your asynchronous calls are taking a long time and you are experiencing "race conditions" because of it (your UI thread is proceeding with the updates on the TextViews before it should), you can solve the situation by changing your button click delegate to an async delegate, and forcing an await on the long operations' result before proceeding.
bClick.Click += async delegate {
//...
Job = await act.executeJob(httpUrl, Ticket);
//...
}

Stripe Checkout does not work on WebView

I'm trying to use Stripe Checkout inside a WebView (both in Android and in iOS).
If I run the demo checkout in this page from Google Chrome from mobile it opens a new web page and everything works fine.
When I try to run the demo from a WebView (which I expect to behave in a completely similar way) it does not work and gives me a
Sorry, there was a problem loading Checkout.
If this persists, please try a different browser.
I thought that it is not made for mobile, but this is not true because from Google Chrome works perfectly fine.
Any suggestions to make it work?
This is the only way I managed to work with Stripe Checkout on webviews (I used Xamarin):
Install Plugin.Share from nuget (project, add newget packages both in shared and specific mobile project folders)
Try out this code to see that checkout works fine:
using System;
using Xamarin.Forms;
using Plugin.Share;
using Plugin.Share.Abstractions;
namespace stripewebviewtest
{
public class App : Application
{
public App ()
{
Button btn = new Button ();
btn.Text = "Click on me!";
// The root page of your application
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
new Label {
XAlign = TextAlignment.Center,
Text = "Welcome to Xamarin Forms!"
},
btn
}
}
};
btn.Clicked += (object sender, EventArgs e) => {
var url = "https://stripe.com/docs/checkout";
CrossShare.Current.OpenBrowser (url, new BrowserOptions {
ChromeShowTitle = true,
ChromeToolbarColor = new ShareColor {
A = 255,
R = 118,
G = 53,
B = 235
},
UseSafairReaderMode = false,
UseSafariWebViewController = false
});
};
}
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
protected override void OnResume ()
{
// Handle when your app resumes
}
}
}
Run the project on a device
Click on "Click on me!" button
Click on "Pay with Card" button on the Webview
↝

Categories

Resources