Hybrid WebView Navigating and navigated events not responding in Xamarin Forms - c#

well I have a Hybrid Webview that is calling a page and when it does I want to be able to handle the errors using the Navigated and Navigating event. This is my code below:
<StackLayout HorizontalOptions="FillAndExpand" BackgroundColor="{Binding ThemeColor}" IsVisible="{Binding IsAdvert}">
<local:HybridWebView x:Name="hybridWebView" Navigated="webOnEndNavigating" Navigating="Webview_Navigating" Uri="{Binding Source}" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</StackLayout>
Then in the .cs code where the event is is:
void webOnEndNavigating(object sender, WebNavigatedEventArgs e)
{
progress.IsVisible = false;
try
{
switch (e.Result)
{
case WebNavigationResult.Cancel:
Navigation.PopAsync();
break;
case WebNavigationResult.Failure:
var isConnected = Connectivity.NetworkAccess;
if (isConnected != NetworkAccess.Internet)
{
Navigation.PushModalAsync(new Offline());
Navigation.PopAsync();
}
break;
case WebNavigationResult.Success:
mnu.IsEmptyList = false;
break;
case WebNavigationResult.Timeout:
Navigation.PushModalAsync(new Offline());
Navigation.PopAsync();
break;
default:
Navigation.PushModalAsync(new Offline());
Navigation.PopAsync();
break;
}
}
catch
{
Navigation.PushModalAsync(new Offline());
Navigation.PopAsync();
}
}
private void Webview_Navigating(object sender, WebNavigatingEventArgs e)
{
progress.IsVisible = true;
}
I have paced breakpoints at each end every case of my switch statement and even on the entry level of my event but I am not getting anything either on the successful rendering of the site I am calling or on the failure or time out. How best Can I handle this?

According to xamarin documents:
On iOS, this HTML file resides in the Content folder of the platform
project, with a build action of BundleResource. On Android, this HTML
file resides in the Assets/Content folder of the platform project,
with a build action of AndroidAsset.
I did a test based on this sample and the method can work. So, you can check your webpage to see if it's in the right place.
For more information, you can refer to this https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/hybridwebview

Related

Xamarin/Tizen: Executing Navigation.PopAsync() crashes the app

I have a navigation page that sets up three pages. The first page loads, the user has to pick an option from a listview and then it loads the second page with PushAsync(). At this point the user can now navigate between the three pages by turning the clock face. If I call PopToRootAsync() on the second page it works fine. If the rotary clock face is turned clockwise it loads a third page via PushAsync().
The problem is if I call PopAsync() on that third page OR I change the PopToRootAsync() on the second page to a PopAsync(), the app crashes. I have no way to determine what the error is either as I just get segmentation fault and nothing is written to the Tizen log that is seemingly indicative of why it crashed.
Is there some reason that a PopAsync() would cause this? I know I saw some other articles this could occur if the MainPage is not loaded into a NavigationPage but I'm doing that. I've been looking through stuff and writing debug logs for days but have nothing to show for it. Any help would be more than appreciated. Thank you!
App.cs
public App()
{
MainPage = new NavigationPage(new ServerSelectionPage());
}
ServerSelection.cs
private void ServerSelection_OnItemTapped(object sender, ItemTappedEventArgs args)
{
App.SERVER = (Server)args.Item;
Navigation.PushAsync(new ArrowsPage());
}
PageBase.cs
public async void Rotate(RotaryEventArgs args)
{
Page _currentPage = Page.REMOTE_BUTTONS;
if (this.GetType() == typeof(ButtonsPage))
_currentPage = Page.REMOTE_BUTTONS;
else if (this.GetType() == typeof(ArrowsPage))
_currentPage = Page.REMOTE_ARROWS;
else
_currentPage = Page.SERVER_SELECTION;
// When rotating (previous rotation is ongoing, do nothing)
if (_rotating)
{
return;
}
_rotating = true;
if (!(App.SERVER is null))
{
if (_currentPage == Page.SERVER_SELECTION)
{
if (args.IsClockwise)
await Navigation.PushAsync(new ArrowsPage());
}
else if (_currentPage == Page.REMOTE_DIRECTIONAL)
{
if (args.IsClockwise)
await Navigation.PushAsync(new ButtonsPage());
else
await Navigation.PopToRootAsync();
}
else
{
try
{
if (!args.IsClockwise)
await Navigation.PopAsync(); // This will crash the app
}
catch(Exception ex)
{
Log.Debug(ex.Message);
}
}
}
_rotating = false;
}
After reading the comment by #vin about checking if the Navigation object is null I suddenly had the thought that it may not be the Navigation page but the RotaryFocusObject. So I changed
if (!args.IsClockwise)
await Navigation.PopAsync();
to
if (!args.IsClockwise)
{
RotaryFocusObject = null;
await Task.Delay(300);
await Navigation.PopAsync();
}
and it no longer crashes. The delay is necessary as if you call PopAsync right after setting the object to null it can still sometimes crash.
So apparently if you pop the current page it causes an error as the focus of the rotary dial is still set to the current navigation page. Why this error does not seemingly occur if you use the Navigation.PopToRootAsync() makes no sense to me.
Navigation.PopToRootAsync() and Navigation.PopToRootAsync() are causing destroy of Page renderer, and the Rotate handler is belong to Page Renderer,
If Page renderer was deleted before completion of Rotate Native Callback that is belong to Page renderer, it will be crashed.
await Task.Delay() let to returning native callback.

My AdMob Ad only loading up when I navigate to another page then back

Is this because of the OnElementChanged event?
Can I load the ads when the app is starting?
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
var adView = new AdView(Context);
switch ((Element as AdBanner).Size)
{
case AdBanner.Sizes.Standardbanner:
adView.AdSize = AdSize.Banner;
break;
...
}
adView.AdUnitId = "ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx";
var requestbuilder = new AdRequest.Builder();
adView.LoadAd(requestbuilder.Build());
SetNativeControl(adView);
}
}
I able to solve same issue by setting the VerticalOptions="FillAndExpand" and HorizontalOptions="FillAndExpand"
To create a native control in OnElementChanged in a renderer, test on e.NewElement != null and Control == null:
base.OnElementChanged(e);
if (e.NewElement != null && Control == null)
{
_adView = CreateNativeControl(...);
SetNativeControl(_adView);
}
According to the renderer documentation, the OnElementChanged method "is called when the Xamarin.Forms custom control is created to render the corresponding native control".
When the App is calling the OnElementChanged method at the start of the program it doesn’t showing up any ads. Only when it called to the second time.
I can't find the reason to issue happens, but after a doze of tries I found a workaround that worked for me, I did put the below code in end of OnAppearing override:
if (!this.initialized)
{
this.initialized = true;
this.adMobView.IsVisible = false;
Task.Run(async () =>
{
await Task.Delay(delay).ConfigureAwait(false);
Device.BeginInvokeOnMainThread(() => this.adMobView.IsVisible = true);
});
}
Basically, I change the visibility of admob view to false and, after some time (test good delay values for you), I change it to true again.
That is far away from a good solution and a best one will be very welcome, but for now it worked for me and can be usefull temporarily.

How to route touch events to separate controls UWP C#

I am facing a major problem for custom touch event handling.
The goal is to route the touch event to different controls, depending on how many fingers are used.
For example:
We have a scrollview with many webviews arranged in it.
The zoomfactor is set to present one webview to fullscreen.
Currently I am using the pointer pressed / released functions like this, to configure the controls, so the event will be catched by the right one:
int pointerCount = 0;
private void ScrollView_PointerPressed(object sender, PointerRoutedEventArgs e)
{
try
{
pointerCount++;
if (pointerCount > 3)
pointerCount = 0;
switch (pointerCount)
{
case 1:
// I don't do anything, so it goes to the webview anyway
break;
case 2:
EnableScrolling();
break;
case 3:
ZoomInOut();
break;
default:
return;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
private void EnableScrolling()
{
ScrollViewer.ZoomMode = ZoomMode.Enabled;
ScrollViewer.VerticalScrollMode = ScrollMode.Enabled;
ScrollViewer.HorizontalScrollMode = ScrollMode.Enabled;
}
1-finger events should go to the webview // this works
2-finger events should go to the ScrollView // this is not working, because the webview grabs the touch event and will not release it again
3-finger events should zoom out // this works
The PointerPressed is always called, but the PointerReleased is not called when the PointerPressed was on the webview.
This also results in the effect of not decreasing the pointerCount, so it is possible to do a 1-finger tap 3 times and it results in the 3-finger event and zooms out, that should not happen.
Hopefully you can see the problem and help to resolve it.
If you think this is a way too wrong approach, feel free to show an alternative that works out better.
Well, I couldn't find a proper solution, but I was able to remove the unwanted side effect with upcounting the pointers event if they were released.
private void ScrollViewer_PointerReleased(object sender, PointerRoutedEventArgs e)
{
pointerCount = 0;
}
So the right direction handling is working fine now, but I still can't do something like:
currentWebView.CapturePointer(e.Pointer);
Because it won't root the pointerEvent into it's content, it will call the pointer events of the WebView, but it won't root it into it's html & js content.
I also tried to use
.... wb.ManipulationStarted += Wb_ManipulationStarted; ....
private void Wb_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
{
// something here
}
but this also will not arrive in the content.
Any ideas for another approach?
I figured out how to handle that, and it is so simple that I could cry...
I just added this line of code to tell the webviews js that there was a event...
await webView.InvokeScriptAsync("eval", new string[] { "document.elementFromPoint(" + pointX + ", " + pointY + ").click();" });
And the webview handles the rest of it. So there is no custom invoke as I was worried about, it's just the standard function invoked, that is always invoked when a touch/click is performed.

Interact with "system-wide" media player

I want to develop a music app for Windows 10 and I'm curious about the interface provided by Groove Music next to the volume bar. I've tried Googling to get more information about it but I haven't had any success whatsoever. When I'm playing music in Groove Music and I raise or lower the volume, the name as well as the artist and album artwork of the current song show up with music controls next to the volume indicator this:
I was wondering how I could create this dialog in my own app and what windows API's I'd have to look into.
I'm going to add my input to this even though there is a great answer already by #Stamos, because I've found that it is actually possible to use SystemMediaTransportControls from a native windows app (not only a universal app).
First thing, it does still require a reference to the universal winmd files, so it will only work on Win10. They will be located in the 10 sdk, and you can add them via the regular Add Reference -> Browse but you may need to change the filter on the bottom right of the dialog to "All Files" for them to show up. They are found here on my PC:
Windows.Foundation.UniversalApiContract: C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.UniversalApiContract\1.0.0.0\Windows.Foundation.UniversalApiContract.winmd
Windows.Foundation.FoundationContract: C:\Program Files (x86)\Windows Kits\10\References\Windows.Foundation.FoundationContract\2.0.0.0\Windows.Foundation.FoundationContract.winmd
After you have the necessary references, you'll run into another problem - you can't access the transport controls via the usual SystemMediaTransportControls.GetForCurrentView(); (it will throw an exception) because you don't actually have a universal view. This is alleviated by using the following:
SystemMediaTransportControls systemControls =
BackgroundMediaPlayer.Current.SystemMediaTransportControls;
After this, feel free to use any of the samples online or Stamos' answer.
You need to use SystemMediaTransportControls
Here is a basic setup with Play and Pause. If you like to enable more controls you can do using the available properties for ex.
systemControls.IsNextEnabled = true;
and you have to add the case in the button switch.
case SystemMediaTransportControlsButton.Next:
//handle next song
break;
xaml
<MediaElement x:Name="mediaElement" Height="100" Width="100" AreTransportControlsEnabled="True"/>
C#
public MainPage()
{
this.InitializeComponent();
systemControls = SystemMediaTransportControls.GetForCurrentView();
// Register to handle the following system transpot control buttons.
systemControls.ButtonPressed += SystemControls_ButtonPressed;
mediaElement.CurrentStateChanged += MediaElement_CurrentStateChanged;
systemControls.IsPlayEnabled = true;
systemControls.IsPauseEnabled = true;
}
private void MediaElement_CurrentStateChanged(object sender, RoutedEventArgs e)
{
switch (mediaElement.CurrentState)
{
case MediaElementState.Playing:
systemControls.PlaybackStatus = MediaPlaybackStatus.Playing;
break;
case MediaElementState.Paused:
systemControls.PlaybackStatus = MediaPlaybackStatus.Paused;
break;
case MediaElementState.Stopped:
systemControls.PlaybackStatus = MediaPlaybackStatus.Stopped;
break;
case MediaElementState.Closed:
systemControls.PlaybackStatus = MediaPlaybackStatus.Closed;
break;
default:
break;
}
}
void SystemControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
PlayMedia();
break;
case SystemMediaTransportControlsButton.Pause:
PauseMedia();
break;
case SystemMediaTransportControlsButton.Stop:
StopMedia();
break;
default:
break;
}
}
private async void StopMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
mediaElement.Stop();
});
}
async void PlayMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
if (mediaElement.CurrentState == MediaElementState.Playing)
mediaElement.Pause();
else
mediaElement.Play();
});
}
async void PauseMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
mediaElement.Pause();
});
}
Output
Also if you want all this to work in background you will have to do a Declaration in Package.appxmanifest for a Background Tasks, enable audio and add an entry point like TestUWP.MainPage
<MediaElement x:Name="Media"
AreTransportControlsEnabled="True">
<MediaElement.TransportControls>
<MediaTransportControls
Style="{StaticResource MediaTCStyle}"/>
</MediaElement.TransportControls>
</MediaElement>
The style is quite big so i'm attaching a link
MediaTransportControls styles and templates
I got the style from the article(link above) and modified it in my own ResourceDictionary.

How to play background audio Windows phone 8.1

I read this article about "How to play audio in the background (XAML)" and works,i played my mp3 file well,but if i try to out from app the music just stop,i thought "background audio" was to play even if the app was not focus on screen!
XAML
<Grid>
<MediaElement x:Name="musicPlayer"
Source="Assets/VIGEVANO.mp3"
AudioCategory="BackgroundCapableMedia"
CurrentStateChanged="MusicPlayer_CurrentStateChanged" />
</Grid>
CS
SystemMediaTransportControls systemControls;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
// Hook up app to system transport controls.
systemControls = SystemMediaTransportControls.GetForCurrentView();
systemControls.ButtonPressed += SystemControls_ButtonPressed;
// Register to handle the following system transpot control buttons.
systemControls.IsPlayEnabled = true;
systemControls.IsPauseEnabled = true;
}
private void SystemControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
{
switch (args.Button)
{
case SystemMediaTransportControlsButton.Play:
PlayMedia();
break;
case SystemMediaTransportControlsButton.Pause:
PauseMedia();
break;
default:
break;
}
}
async void PlayMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
musicPlayer.Play();
});
}
async void PauseMedia()
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
musicPlayer.Pause();
});
}
What i miss?
Summary answer reached
Since Background Streaming was the main requirement so the solution reached was
Yes Check out this sample and let me know if it works for you.
Your background task first tutorial works for local mp3 files but streaming audio is a different scenario in itself
Also for streaming we used to use PhoneSM

Categories

Resources