How can I enable video autoplay in WKWebView on iOS - xamarin

I have a WebView that contains an iframe (an HTML element). this iframe has a Peertube video plyaer, I pass &autoplay=1 to that iframe and it works good on android, iPad and browser, but on iPhone the video can not be autoplayed.
I tried custom render with WKWebView with this code:
class FullScreenEnabledWebViewRenderer : WkWebViewRenderer
{
WKUserContentController userController;
public FullScreenEnabledWebViewRenderer() : this(new WKWebViewConfiguration() { MediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypes.None, AllowsInlineMediaPlayback = true, MediaPlaybackRequiresUserAction = false, RequiresUserActionForMediaPlayback = false })
{
}
public FullScreenEnabledWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
config.AllowsInlineMediaPlayback = true;
config.AllowsAirPlayForMediaPlayback = true;
config.AllowsPictureInPictureMediaPlayback = true;
config.MediaPlaybackAllowsAirPlay = true;
config.MediaPlaybackRequiresUserAction = false;
config.RequiresUserActionForMediaPlayback = false;
config.MediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypes.None;
}
}
and still does not work.
Can you help me with that please.

Webview in iOS couldn't support iframe very well . So it could better to use <video> like
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = #"<html><body><video width='300' height='500' muted='muted' controls autoplay='autoplay'><source src='https://ia800201.us.archive.org/12/items/BigBuckBunny_328/BigBuckBunny_512kb.mp4' type='video/mp4'></video></body></html>";
webview.Source = htmlSource;
If it still could not auto play in iOS , we could invoked JS to play it .
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using App4;
using App4.iOS;
using WebKit;
using ObjCRuntime;
[assembly: ExportRenderer(typeof(WebView), typeof(FullScreenEnabledWebViewRenderer))]
namespace App4.iOS
{
public class FullScreenEnabledWebViewRenderer: ViewRenderer<WebView, WKWebView>
{
WKWebView wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var config = new WKWebViewConfiguration();
config.AllowsInlineMediaPlayback = true;
config.AllowsAirPlayForMediaPlayback = true;
config.AllowsPictureInPictureMediaPlayback = true;
config.MediaPlaybackAllowsAirPlay = true;
config.MediaPlaybackRequiresUserAction = false;
config.RequiresUserActionForMediaPlayback = false;
config.MediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypes.None;
wkWebView = new WKWebView(Frame, config);
wkWebView.NavigationDelegate = new WkWebviewPolicy();
SetNativeControl(wkWebView);
}
}
}
public class WkWebviewPolicy : WKNavigationDelegate
{
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, WKWebpagePreferences preferences, [BlockProxy(typeof(NIDActionArity2V120))] Action<WKNavigationActionPolicy, WKWebpagePreferences> decisionHandler)
{
decisionHandler.Invoke(WKNavigationActionPolicy.Allow, preferences);
//base.DecidePolicy(webView, navigationAction, preferences, decisionHandler);
}
public override void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{
base.DidFinishNavigation(webView, navigation);
var JSstr = #"var videos = document.querySelectorAll('video'); for (var i = videos.length - 1; i >= 0; i--) { var ivideo = videos[i]; ivideo.setAttribute('webkit-playsinline','\'); ivideo.play(); };";
webView.EvaluateJavaScript(JSstr,null);
}
}
}

Related

xamarin WKWebView, ipad does not support youtube full screen

I implemented a webview using the WkWebview renderer.
Fullscreen works fine on ipone. But it doesn't work on iPad.
An image like the one below will appear.
I saw an article saying to do webViewConfiguration.allowsInlineMediaPlayback = true and applied it but it didn't work. What's the problem?
Here is my code.
public class MyWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler, IWKNavigationDelegate
{
WKUserContentController userController;
public MyWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public MyWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
userController = config.UserContentController;
var script = new WKUserScript(new NSString(_JavascriptFunction_CSharpOpenWeb), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction_CSharpOpenWeb");
}
protected async override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction_CSharpOpenWeb");
MyWebView myWebView = e.OldElement as MyWebView;
myWebView.Cleanup();
}
if (e.NewElement != null)
{
this.NavigationDelegate = new MyNavigationDelegate(this);
var webView = (MyWebView)Element;
webView.UriChanged += async (s1, e1) =>
{
NSUrl nsurl = new NSUrl(webView.Uri);
Configuration.AllowsInlineMediaPlayback = true;
NSMutableUrlRequest request = new NSMutableUrlRequest(nsurl);
await SetCookies();
LoadRequest(request);
};
if (!string.IsNullOrEmpty(webView.Uri))
{
NSUrl nsurl = new NSUrl(webView.Uri);
NSMutableUrlRequest request = new NSMutableUrlRequest(nsurl);
await SetCookies();
LoadRequest(request);
}
}
}
}
Can you tell me what I did wrong?
Configuration.AllowsInlineMediaPlayback = true; is the OPPOSITE of what you want.
Set it to false:
Configuration.AllowsInlineMediaPlayback = false;
If that doesn't fix it, then you may need to do that earlier in the code.
See if it works when done in the constructors:
public MyWebViewRenderer() : this(new WKWebViewConfiguration())
{
EnsureFullScreen();
...
}
public MyWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
EnsureFullScreen();
...
}
private void EnsureFullScreen()
{
if (Control.Configuration.AllowsInlineMediaPlayback)
Configuration.AllowsInlineMediaPlayback = false;
}

Display SplashScreen during webview loading

I'm building a Xamarin app that display a webview; currenlty I have my splash screen displayed when the application starts using the code on MainActivity.cs:
[Activity(Label = "App.Xamarin",
Icon = "#mipmap/icon",
Theme = "#style/Theme.Splash",
MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
Now I need to display the same splash in my webviewrenderer when the webvieb starts/stop to load its content, so I have my own WebViewClient:
internal class CusViewClient : WebViewClient
{
string _javascript;
public SistemiWebViewClient(string javascript)
{
_javascript = javascript;
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
view.Visibility = ViewStates.Invisible;
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
view.Visibility = ViewStates.Visible;
}
}
but I can't find the correct way to display the splash.
I ended up using a progress-bar instead of splash screen image to display the progress loading of the page in the chrome client, the code is the following:
public class CusWebChromeClient : WebChromeClient
{
Android.Widget.ProgressBar progressBar;
public SistemiWebChromeClient(Android.Widget.ProgressBar progressBar)
{
this.progressBar = progressBar;
}
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
if (newProgress < 100 && progressBar.Visibility == ViewStates.Gone)
{
progressBar.Visibility = ViewStates.Visible;
}
progressBar.SetProgress(newProgress, true);
if (newProgress == 100)
{
progressBar.Visibility = ViewStates.Gone;
}
}
}

using xam.Plugin.DownloadManager Xamarin forms

i am using xam.plugin.downloadmanager to download files in my app,once file download i wnats to redirect to downloads native page to view which i was download from server. is there any way ?? plesae help me
this is my code Anroid main activity class
using Android.OS;
using Plugin.DownloadManager;
using Plugin.DownloadManager.Abstractions;
using Xamarin.Forms.PlatformConfiguration;
using System.Linq;
using System.IO;
namespace Expertential.Droid
{
[Activity(Label = "Expertential", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Downloaded();
global::Xamarin.Forms.Forms.Init(this, bundle);
Rg.Plugins.Popup.Popup.Init(this, bundle);
LoadApplication(new App());
}
public void Downloaded()
{
CrossDownloadManager.Current.PathNameForDownloadedFile = new System.Func<IDownloadFile, string>(file =>
{
string fileName = Android.Net.Uri.Parse(file.Url).Path.Split('/').Last();
return Path.Combine(ApplicationContext.GetExternalFilesDir(Android.OS.Environment.DirectoryDownloads).AbsolutePath, fileName);
});
}
}
}
viewmodel in here iam calling download .netstandard library
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using Expertential.Models;
using Expertential.Services;
using Expertential.ViewModels;
using Plugin.DownloadManager;
using Plugin.DownloadManager.Abstractions;
using Rg.Plugins.Popup.Services;
using Xamarin.Forms;
namespace Expertential.ViewModels
{
class CustomPopupViewModel :BaseViewModel
{
private CustomPopupViewModelService customPopupViewModelService;
private ObservableCollection<Attachments> attachment;
public ObservableCollection<Attachments> Attachment
{
get { return attachment; }
set
{
attachment = value;
RaisePropertyChanged(nameof(Attachment));
}
}
public CustomPopupViewModel()
{
}
private int _requertAttHeight;
public int requertAttHeight { get { return _requertAttHeight; }
set { _requertAttHeight = value;
RaisePropertyChanged(nameof(requertAttHeight));
}
}
private Boolean _taskLoader;
public Boolean taskLoader
{
get { return _taskLoader; }
set
{
_taskLoader = value;
RaisePropertyChanged(nameof(taskLoader));
}
}
public IDownloadFile File;
Boolean isDownloading = true;
#region custom function
public void GetAttachments(List<Attachments> attachment)
{
Attachment = new ObservableCollection<Attachments>();
if (attachment.Count > 0)
{
foreach (Attachments att in attachment)
{
var title = att.title;
string[] titleArray = title.Split('.');
if (titleArray.Length > 1)
{
setIconVisible(titleArray[1],att);
}
Attachment.Add(att);
}
requertAttHeight = 40 * attachment.Count;
}
}
public void setIconVisible(string content, Attachments att)
{
switch (content)
{
case "png":
case "jpg":
case "jpeg":
att.isWord = false;
att.isExcel = false;
att.isPdf = false;
att.isPpt = false;
att.isImage = true;
break;
case "ppt":
case "pptx":
att.isWord = false;
att.isExcel = false;
att.isPdf = false;
att.isPpt = true;
att.isImage = false;
break;
case "xlsx":
case "xls":
att.isWord = false;
att.isExcel = true;
att.isPdf = false;
att.isPpt = false;
att.isImage = false;
break;
case "docx":
att.isWord = true;
att.isExcel = false;
att.isPdf = false;
att.isPpt = false;
att.isImage = false;
break;
}
}
public async Task<string> GetFileFullUrl(string fileName)
{
var strValue = "";
customPopupViewModelService = new CustomPopupViewModelService();
strValue = await customPopupViewModelService.GetFileFullUrl(fileName);
return strValue;
}
public async void DownloadFile(string FileName)
{
Boolean target = false;
this.taskLoader = true;
var DownloadManager = CrossDownloadManager.Current;
var file = DownloadManager.CreateDownloadFile(FileName);
await Task.Yield();
await Task.Run(() =>
{
DownloadManager.Start(file, true);
while (isDownloading)
{
Task.Delay(100);
isDownloading = IsDownloading(file);
}
});
if (!isDownloading)
{
this.taskLoader = false;
target = await Application.Current.MainPage.DisplayAlert("Alert", "File Download Goto Download page to view", "Ok", "Cancel");
}
if (target)
{
// Device.OpenUri(new Uri("Downloads"));
try
{
await PopupNavigation.Instance.PopAsync();
// here i want to redirect to downloads app ->
// Intent myIntent = new Intent(Android.App.DownloadManager.ActionDownloadComplete);
}
catch (Exception e)
{
//// TODO: handle exception
//String data = e.getMessage();
}
// Environment.GetFolderPath(Environment.SpecialFolder.)
//var x = file.DestinationPathName;
//Device.OpenUri(new Uri(x));
}
}
public Boolean IsDownloading(IDownloadFile file)
{
if (file == null) return false;
switch (file.Status)
{
case DownloadFileStatus.INITIALIZED:
case DownloadFileStatus.PAUSED:
case DownloadFileStatus.PENDING:
case DownloadFileStatus.RUNNING:
return true;
case DownloadFileStatus.COMPLETED:
case DownloadFileStatus.CANCELED:
case DownloadFileStatus.FAILED:
return false;
default:
return false;
}
}
public void AbortDownloading()
{
CrossDownloadManager.Current.Abort(File);
}
#endregion
}
}
My understanding of your question tells me you want to open the default downloads application that Android has and it can be done as follows:
Intent intent = new Intent(DownloadManager.ActionViewDownloads);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);
Yes this is what i expected, i accomplished with some changes in your code
Intent intent = new Intent(DownloadManager.ActionViewDownloads);
intent.AddFlags(ActivityFlags.NewTask);
Android.App.Application.Context.StartActivity(intent);

How can I change the font for the header of a Navigation page with Xamarin Forms?

I can change the font color like this:
var homePage = new NavigationPage(new HomePage())
{
Title = "Home",
Icon = "ionicons_2_0_1_home_outline_25.png",
BarTextColor = Color.Gray,
};
But is there a way to change the font for the Title. I would like to change it for the iOS and Android platforms only. Hoping that someone knows of custom renderer code that can help me to do this.
You need Custom Renderer , refer to this sample
iOS
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustomNavigationPageRenderer))]
namespace CustomFontsNavigationPage.iOS.Renderers
{
public class CustomNavigationPageRenderer : NavigationRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var att = new UITextAttributes();
UIFont customFont = UIFont.FromName("Trashtalk", 20);
UIFont systemFont = UIFont.SystemFontOfSize(20.0);
UIFont systemBoldFont = UIFont.SystemFontOfSize(20.0 , FontAttributes.Bold);
att.Font = font;
UINavigationBar.Appearance.SetTitleTextAttributes(att);
}
}
}
}
Android
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(CustomNavigationPageRenderer))]
namespace CustomFontsNavigationPage.Droid.Renderers
{
public class CustomNavigationPageRenderer : NavigationPageRenderer
{
private Android.Support.V7.Widget.Toolbar _toolbar;
public override void OnViewAdded(Android.Views.View child)
{
base.OnViewAdded(child);
if (child.GetType() == typeof(Android.Support.V7.Widget.Toolbar))
{
_toolbar = (Android.Support.V7.Widget.Toolbar)child;
_toolbar.ChildViewAdded += Toolbar_ChildViewAdded;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if(disposing)
{
_toolbar.ChildViewAdded -= Toolbar_ChildViewAdded;
}
}
private void Toolbar_ChildViewAdded(object sender, ChildViewAddedEventArgs e)
{
var view = e.Child.GetType();
if (e.Child.GetType() == typeof(Android.Widget.TextView))
{
var textView = (Android.Widget.TextView)e.Child;
var spaceFont = Typeface.CreateFromAsset(Forms.Context.ApplicationContext.Assets, "Trashtalk.ttf");
var systemFont = Typeface.DEFAULT;
var systemBoldFont = Typeface.DEFAULT_BOLD;
textView.Typeface = spaceFont;
_toolbar.ChildViewAdded -= Toolbar_ChildViewAdded;
}
}
}
}
There is no need in a custom renderer on iOS, you can just use the Appearance API:
UINavigationBar.Appearance.SetTitleTextAttributes(new UITextAttributes
{
Font = UIFont.FromName("MyCoolFont", 20)
});
In Android you do need a renderer, however you should check against Android.Support.V7.Widget.AppCompatTextView and not Android.Widget.TextView.
Tested on Xamarin.Forms 3.4.0

Scanner is null and not accessible

I am attempting to read a QRCode in Xamarin.Forms. I have a shared project in XF. I have added the nuget packages for ZXing.Net. Everything works in the iOS project. I am getting an error in the Android project. The errors that I get via Android SDK Monitor, it indicates that there is a problem with the scanner being null and not being accessible. I am guessing that there is something that I have not set up correct on the Android side. Does anyone see anything improper in my code? Thanks for your time.
ScanPage class:
public class ScanPage : ContentPage
{
ZXing.Net.Mobile.Forms.ZXingScannerView zxing;
ZXingDefaultOverlay overlay;
bool isConnected = false;
string basicUrl = "golfeventscores.azurewebsites.net";
public ScanPage ()
{
zxing = new ZXing.Net.Mobile.Forms.ZXingScannerView
{
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
AutomationId = "zxingScannerView",
};
zxing.OnScanResult += async (ZXing.Result result) => {
zxing.IsAnalyzing = false;
zxing.IsScanning = false;
var teamToken = result.Text;
//MessagingCenter.Send<string>(teamToken, "SelectTeamMembers");
isConnected = await Plugin.Connectivity.CrossConnectivity.Current.IsRemoteReachable(basicUrl);
if (isConnected)
{
await GetTeamData(teamToken);
}
else
{
await DisplayAlert("Connectivity", "There is a problem with internet connectivity. Please try and reload this screen.", "Ok");
}
};
overlay = new ZXingDefaultOverlay
{
TopText = "Hold your phone up to the barcode",
BottomText = "Scanning will happen automatically",
ShowFlashButton = zxing.HasTorch,
AutomationId = "zxingDefaultOverlay",
};
overlay.FlashButtonClicked += (sender, e) => {
zxing.IsTorchOn = !zxing.IsTorchOn;
};
var grid = new Grid
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
};
grid.Children.Add(zxing);
grid.Children.Add(overlay);
// The root page of your application
Content = grid;
}
protected override void OnAppearing()
{
base.OnAppearing();
zxing.IsScanning = true;
}
protected override void OnDisappearing()
{
zxing.IsScanning = false;
base.OnDisappearing();
}
async System.Threading.Tasks.Task GetTeamData(string Token)
{
try
{
var scanResult = await WebServices.ws.TokenLookup(Token);
if (scanResult.Result == true)
{
if (scanResult.IsScoreBoard == true)
{
var uri = new System.Uri(scanResult.ScoreboardUrl);
Device.BeginInvokeOnMainThread(() =>
{
Device.OpenUri(uri);
Navigation.PopToRootAsync();
});
}
if (scanResult.IsCharity == true)
{
if (scanResult.TeamPlayers.Count > 0)
{
var player = scanResult.TeamPlayers.First();
var playerId = player.PlayerTeamId;
var urlResult = await WebServices.ws.ServerUrl(Token, playerId);
if (urlResult.ValidRequest && (!String.IsNullOrEmpty(urlResult.Url)))
{
var uri = new System.Uri(urlResult.Url);
Device.OpenUri(uri);
await Navigation.PopToRootAsync();
}
}
else{
await DisplayAlert("Scanning", "There was a problem downloading the Charity Team Info.", "OK");
}
}
else
{
if (scanResult.IsLargeGame != true)
{
var select = new Pages.SelectTeamMembers(Token);
await Navigation.PushAsync(select);
}
else
{
await DisplayAlert("Large Game", "Don't have the large team game setup with scanning.", "Ok");
}
}
}
else
{
await DisplayAlert("Server Problem", "There was some type of server error. Please try again or call Wally.", "Ok");
}
}
catch(System.Exception sysExc)
{
//nothing seems to be caught
}
}
}
MainActivity.cs contents:
[Activity (Label = "TD Scan", Icon = "#drawable/icon", Theme="#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate (Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.Init (this, bundle);
ZXing.Net.Mobile.Forms.Android.Platform.Init();
LoadApplication (new GolfGameScanApp.App ());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Have you defined all in Android Project?
Xamarin Forms
For Xamarin Forms there is a bit more setup needed. You will need to initialize the library on each platform in your platform specific app project.
Android
On Android, in your main Activity's OnCreate (..) implementation, call:
ZXing.Net.Mobile.Forms.Android.Platform.Init();
ZXing.Net.Mobile for Xamarin.Forms also handles the new Android permission request model for you, but you will need to add the following override implementation to your main Activity as well:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
global::ZXing.Net.Mobile.Forms.Android.PermissionsHandler.OnRequestPermissionsResult (requestCode, permissions, grantResults);
}
The Camera permission should be automatically included for you in the AndroidManifest.xml however if you would like to use the Torch API's you will still need to add the Flashlight permission yourself. You can do this by using the following assembly level attribute:
[assembly: UsesPermission (Android.Manifest.Permission.Flashlight)]

Resources