How to play a system sound in Xamarin.Forms - xamarin

I'm looking for lightweight built in system cross platform sound player. Something that could beep like System.Console.Beep and which i could invoke directly from my view model.
I'm aware that i could use a provider (strategy) pattern to coin the platform specific implementations or to use some media player like XamarinMediaManager.

You can use Dependency service
ANDROID
public class AudioService : IAudio
{
private MediaPlayer _mediaPlayer;
public bool PlayFile()
{
_mediaPlayer = MediaPlayer.Create(global::Android.App.Application.Context, Resource.Raw.test);
_mediaPlayer.Start();
return true;
}
}
IOS
public class AudioService : IAudio
{
private AVAudioPlayer _ringtoneAudioPlayer;
public AudioService()
{
_ringtoneAudioPlayer = AVAudioPlayer.FromUrl(NSUrl.FromFilename("call.caf"));
_ringtoneAudioPlayer.NumberOfLoops = -1; // infinite
}
public void PlayFile()
{
if (_ringtoneAudioPlayer != null)
{
_ringtoneAudioPlayer.Stop();
}
_ringtoneAudioPlayer.Play();
}
}
UWP
public class AudioService : IAudio
{
public async Task PlayAudioUWP(string fileName)
{
StorageFolder Folder = await Package.Current.InstalledLocation.GetFolderAsync("Assets");
StorageFile sf = await Folder.GetFileAsync(fileName);
var PlayMusic = new MediaElement();
PlayMusic.AudioCategory = Windows.UI.Xaml.Media.AudioCategory.Media;
PlayMusic.SetSource(await sf.OpenAsync(FileAccessMode.Read), sf.ContentType);
PlayMusic.Play();
}
}
If you want you can try and this plugin
https://www.nuget.org/packages/XamarinAudioManager/

Related

ShouldDelayBannerRenderingListenerImplementor Error after Xamarin AdMob SDK 118.1.1 update

After a long wait for Admob SDK Update on Xamarin to implement Adaptive Banners and new Native Ads ,after upgrade to new Nuget package Sdk 118.1.1preview ,Visual Studio open a .Java File and mention the following error :
error: ShouldDelayBannerRenderingListenerImplementor is not abstract and does not override abstract method zzb(Runnable) in ShouldDelayBannerRenderingListener.
To take advantage of the new xamarin admob sdk (118.1.1 preview) , and overcome the JAVA error :
error: ShouldDelayBannerRenderingListenerImplementor is not abstract and does not override abstract method zzb(Runnable) in ShouldDelayBannerRenderingListener.
just put the following code on the java file poped-up and build solution :
#Override
public boolean zzb(Runnable runnable) {
return false;
}
or let this c# code (FileWatcher)keep running :
static void Main(string[] args)
{
var Javacode = File.ReadAllText(#"MyJavaFile.java");
while (true)
{
try
{
var JavaFilePah = #"C:\Project\Android\apps\AppName\obj\Debug\100\android\src\mono\com\google\android\gms\ads\formats\ShouldDelayBannerRenderingListenerImplementor.java";
DateTime LastFileChang = File.GetLastWriteTime(JavaFilePah);
if (LastFileChang != LastFileChangesave)
{
if (File.Exists(JavaFilePah) && File.ReadAllText(JavaFilePah).Contains("zzb") == false)
{
File.WriteAllText(JavaFilePah, Javacode);
Console.WriteLine("ok");
}
LastFileChangesave = LastFileChang;
}
Thread.Sleep(10);
}
catch (Exception e)
{
}
}
}
change JavaFilePah with path of ur android app.
MyJavaFile content is :
package mono.com.google.android.gms.ads.formats;
public class ShouldDelayBannerRenderingListenerImplementor
extends java.lang.Object
implements
mono.android.IGCUserPeer,
com.google.android.gms.ads.formats.ShouldDelayBannerRenderingListener
{
/** #hide */
public static final String __md_methods;
static {
__md_methods =
"";
mono.android.Runtime.register ("Android.Gms.Ads.Formats.IShouldDelayBannerRenderingListenerImplementor, Xamarin.GooglePlayServices.Ads.Lite", ShouldDelayBannerRenderingListenerImplementor.class, __md_methods);
}
public ShouldDelayBannerRenderingListenerImplementor ()
{
super ();
if (getClass () == ShouldDelayBannerRenderingListenerImplementor.class)
mono.android.TypeManager.Activate ("Android.Gms.Ads.Formats.IShouldDelayBannerRenderingListenerImplementor, Xamarin.GooglePlayServices.Ads.Lite", "", this, new java.lang.Object[] { });
}
private java.util.ArrayList refList;
public void monodroidAddReference (java.lang.Object obj)
{
if (refList == null)
refList = new java.util.ArrayList ();
refList.add (obj);
}
public void monodroidClearReferences ()
{
if (refList != null)
refList.clear ();
}
#Override
public boolean zzb(Runnable runnable) {
return false;
}
}

android media picker from Xamarin forms

I am writting an application with xamarin forms for iOS and Android.
I want to pick a photo from image gallery.
I have created an android specific static helper:
var i = new Intent();
i.SetType("*/*");
Forms.Context.StartActivity(Intent.CreateChooser(i, ""));
But i have no way to get the selected picture bytes.
I have seen on android tutorials i should implement onActivityResult, but i am not on an activity, this is a specific static call...
Thanks
Via a Form's dependency service:
Create your dependency interface (IMediaPicker)
Create a Activity subclass (MediaChooserActivityProxy) that will act as your Intent.ActionPick proxy
In your Xamarin.Android implementation of the IMediaPicker, use a AutoResetEvent to convert the Android StartActivityForResult / OnActivityResult callback to an await-able synchronous flow.
Dependency Service Interace:
public interface IMediaPicker
{
Task<string> ChooseAFileAsync();
}
Android Dependency Implementation:
public class MediaPicker : IMediaPicker
{
public static string filePickedPath;
public static AutoResetEvent waitHandle;
public async Task<string> ChooseAFileAsync()
{
waitHandle = new AutoResetEvent(false);
filePickedPath = "";
Forms.Context.StartActivity(typeof(MediaChooserActivityProxy));
await Task.Run(() => waitHandle.WaitOne());
return filePickedPath;
}
}
The Proxy/Pseudo Activity to capture OnActivityResult:
public class MediaChooserActivityProxy : Activity
{
const string mimeType = "image/*";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
var intent = new Intent(Intent.ActionPick);
intent.SetType(mimeType);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
{
intent.PutExtra(Intent.ExtraMimeTypes, mimeType);
}
StartActivityForResult(Intent.CreateChooser(intent, "StackOverflow"), 73);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == 73)
if (resultCode == Result.Ok)
{
string[] filePathColumn = { MediaStore.Images.ImageColumns.Data };
var cursor = ContentResolver.Query(data.Data, filePathColumn, null, null, null);
cursor.MoveToFirst();
var colummIndex = cursor.GetColumnIndex(filePathColumn[0]);
MediaPicker.filePickedPath = cursor.GetString(colummIndex);
}
MediaPicker.waitHandle.Set();
Finish();
}
}
Note: This can be implemented on the MainActivity/FormsAppCompatActivity to avoid this additional Activity if desired...
Usage:
var filePath = await DependencyService.Get<IMediaPicker>().ChooseAFileAsync();
System.Diagnostics.Debug.WriteLine(filePath);

Caliburn.Micro 3.0 equivalent to Xamarin.Forms Navigation.PushModalAsync

Does Caliburn.Micro 3.0 (and Caliburn.Micro.Xamarin.Forms) implement functionality to mimic/support Navigation.PushModalAsync in Xamarin.Forms?
No. It's not build in, but its easy to enhance it. Usually, MvvM frameworks are navigating by ViewModels. Caliburn is following this pattern. So it needs some kind of navigation service. This navigationservice is responsible for creating the Views for the ViewModels and call the view framework (Xamarin.Froms in our case) specific navigation functions. NavigationPageAdapter is the thing we are searching for. Now let's enhance it.
public interface IModalNavigationService : INavigationService
{
Task NavigateModalToViewModelAsync<TViewModel>(object parameter = null, bool animated = true);
// TODO: add more functions for closing
}
public class ModalNavigationPageAdapter : NavigationPageAdapter, IModalNavigationService
{
private readonly NavigationPage _navigationPage;
public ModalNavigationPageAdapter(NavigationPage navigationPage) : base(navigationPage)
{
_navigationPage = navigationPage;
}
public async Task NavigateModalToViewModelAsync<TViewModel>(object parameter = null, bool animated = true)
{
var view = ViewLocator.LocateForModelType(typeof(TViewModel), null, null);
await PushModalAsync(view, parameter, animated);
}
private Task PushModalAsync(Element view, object parameter, bool animated)
{
var page = view as Page;
if (page == null)
throw new NotSupportedException(String.Format("{0} does not inherit from {1}.", view.GetType(), typeof(Page)));
var viewModel = ViewModelLocator.LocateForView(view);
if (viewModel != null)
{
TryInjectParameters(viewModel, parameter);
ViewModelBinder.Bind(viewModel, view, null);
}
page.Appearing += (s, e) => ActivateView(page);
page.Disappearing += (s, e) => DeactivateView(page);
return _navigationPage.Navigation.PushModalAsync(page, animated);
}
private static void DeactivateView(BindableObject view)
{
if (view == null)
return;
var deactivate = view.BindingContext as IDeactivate;
if (deactivate != null)
{
deactivate.Deactivate(false);
}
}
private static void ActivateView(BindableObject view)
{
if (view == null)
return;
var activator = view.BindingContext as IActivate;
if (activator != null)
{
activator.Activate();
}
}
}
We just declared the interface IModalNavigationService that extends INavigationService and implement it in our ModalNavigationPageAdapter. Unfortunately Caliburn made alot of functions private, so we have to copy them over to our inherited version.
In caliburn you can navigate via navigationservice.For<VM>().Navigate(). We want to follow this style, so we have to implement something like navigationservice.ModalFor<VM>().Navigate() which we do in an extension method.
public static class ModalNavigationExtensions
{
public static ModalNavigateHelper<TViewModel> ModalFor<TViewModel>(this IModalNavigationService navigationService)
{
return new ModalNavigateHelper<TViewModel>().AttachTo(navigationService);
}
}
This method returns a ModalNavigateHelperthat simplifies the usage of our navigation service (similar to Caliburn's NavigateHelper). It's nearly a copy, but for the IModalNavigationService.
public class ModalNavigateHelper<TViewModel>
{
readonly Dictionary<string, object> parameters = new Dictionary<string, object>();
IModalNavigationService navigationService;
public ModalNavigateHelper<TViewModel> WithParam<TValue>(Expression<Func<TViewModel, TValue>> property, TValue value)
{
if (value is ValueType || !ReferenceEquals(null, value))
{
parameters[property.GetMemberInfo().Name] = value;
}
return this;
}
public ModalNavigateHelper<TViewModel> AttachTo(IModalNavigationService navigationService)
{
this.navigationService = navigationService;
return this;
}
public void Navigate(bool animated = true)
{
if (navigationService == null)
{
throw new InvalidOperationException("Cannot navigate without attaching an INavigationService. Call AttachTo first.");
}
navigationService.NavigateModalToViewModelAsync<TViewModel>(parameters, animated);
}
}
Last but not least, we have to use our shiny new navigation service instead of the old one. The App class is registering the NavigationPageAdapter for the INavigationService as singleton in PrepareViewFirst. We have to change it as follows
public class App : FormsApplication
{
private readonly SimpleContainer container;
public App(SimpleContainer container)
{
this.container = container;
container
.PerRequest<LoginViewModel>()
.PerRequest<FeaturesViewModel>();
Initialize();
DisplayRootView<LoginView>();
}
protected override void PrepareViewFirst(NavigationPage navigationPage)
{
var navigationService = new ModalNavigationPageAdapter(navigationPage);
container.Instance<INavigationService>(navigationService);
container.Instance<IModalNavigationService>(navigationService);
}
}
We are registering our navigation service for INavigationService and IModalNavigationService.
As you can see in the comment, you have to implement close functions that call PopModalAsync by yourself.

How to load PDF in Xamarin Forms

I have a Xamarin Forms app where I want to open a locally stored PDF. I don't need to load them within the app, I'm fine with shelling out to the device's default document viewer for PDFs. How can I do this?
I tried sending a WebView to the PDF, but that didn't work, I just got a blank page.
I've recently done this in my own project using a custom renderer. First implement an empty Xamarin forms view such as (I've included a bindable FilePath attribute):
public class PdfViewer : View
{
public static readonly BindableProperty FilePathProperty =
BindableProperty.Create<DocumentViewer, string>(p => p.FilePath, null);
public string FilePath
{
get
{
return (string)this.GetValue(FilePathProperty);
}
set
{
this.SetValue(FilePathProperty, value);
}
}
}
Then create an iOS Renderer that will be registered for this control. This renderer can, as it is within an iOS project, use the Quick Look Preview Controller to delegate to the built in iOS pdf viewer:
[assembly: ExportRenderer(typeof(PdfViewer), typeof(DocumentViewRenderer))]
public class DocumentViewRenderer
: ViewRenderer<PdfViewer, UIView>
{
private QLPreviewController controller;
protected override void OnElementChanged(ElementChangedEventArgs<DocumentViewer> e)
{
base.OnElementChanged(e);
this.controller = new QLPreviewController();
this.controller.DataSource = new DocumentQLPreviewControllerDataSource(e.NewElement.FilePath);
SetNativeControl(this.controller.View);
}
private class DocumentQLPreviewControllerDataSource : QLPreviewControllerDataSource
{
private string fileName;
public DocumentQLPreviewControllerDataSource(string fileName)
{
this.fileName = fileName;
}
public override int PreviewItemCount(QLPreviewController controller)
{
return 1;
}
public override QLPreviewItem GetPreviewItem(QLPreviewController controller, int index)
{
var documents = NSBundle.MainBundle.BundlePath;
var library = Path.Combine(documents, this.fileName);
NSUrl url = NSUrl.FromFilename(library);
return new QlItem(string.Empty, url);
}
private class QlItem : QLPreviewItem
{
public QlItem(string title, NSUrl uri)
{
this.ItemTitle = title;
this.ItemUrl = uri;
}
public override string ItemTitle { get; private set; }
public override NSUrl ItemUrl { get; private set; }
}
}
}
I haven't compiled and run this as I've extracted it from my larger project but in general this should work.
I had to do something and solve it using a DependencyService . You can use it to open the pdf depending on each platform
I show you an example of how to solve it on Android :
IPdfCreator.cs:
public interface IPdfCreator
{
void ShowPdfFile();
}
MainPage.cs:
private void Button_OnClicked(object sender, EventArgs e)
{
DependencyService.Get<IPdfCreator>().ShowPdfFile();
}
PdfCreatorAndroid.cs
[assembly: Dependency(typeof(PdfCreatorAndroid))]
namespace Example.Droid.DependecyServices
{
public class PdfCreatorAndroid : IPdfCreator
{
public void ShowPdfFile()
{
var fileLocation = "/sdcard/Template.pdf";
var file = new File(fileLocation);
if (!file.Exists())
return;
var intent = DisplayPdf(file);
Forms.Context.StartActivity(intent);
}
public Intent DisplayPdf(File file)
{
var intent = new Intent(Intent.ActionView);
var filepath = Uri.FromFile(file);
intent.SetDataAndType(filepath, "application/pdf");
intent.SetFlags(ActivityFlags.ClearTop);
return intent;
}
}
}
Result:
http://i.stack.imgur.com/vrwzt.png
Here there is a good project that uses MuPDF Library in xamarin . I've tested it and it works properly.
With MuPDF you can zoom out , zoom in and even write some note on PDFs.

MvvmCross plugin for setting up Alarms

I want to write a cross mobile platform app that sets up the alarm by specifying the required parameters like Date and Time. I just want to set up only one time and not repeatedly.
I was unable to find any readily available plugin in mvvmcross or in Xamarin ?
Please help
Since there is no existing plugin within MVVMCross, you may want to write your own plugin. You can find the documentation here:
https://github.com/MvvmCross/MvvmCross/wiki/MvvmCross-plugins
Because you'd like to specify a few parameters, you'd want to see the following section:
https://github.com/MvvmCross/MvvmCross/wiki/MvvmCross-plugins#writing-a-configurable-plugin
Overall this is what you might do:
General Interface
public interface IAlarm
{
void SetupAlarm();
}
public class PluginLoader
: IMvxPluginLoader
{
public static readonly PluginLoader Instance = new PluginLoader();
public void EnsureLoaded()
{
var manager = Mvx.Resolve<IMvxPluginManager>();
manager.EnsurePlatformAdaptionLoaded<PluginLoader>();
}
}
Android Implementation
public class DroidAlarmConfiguration
: IMvxPluginConfiguration
{
public AlarmLength { get; set;}
}
public class DroidAlarm : IAlarm
{
public TimeSpan AlarmLength { get; set; }
public void SetupAlarm()
{
//ALARM IMPLEMENTATION HERE. NOTE THIS IS SOME JAVA SYNTAX!!!!
var globals = Mvx.Resolve<Cirrious.CrossCore.Droid.IMvxAndroidGlobals>();
var alarm = globals.ApplicationContext
.GetSystemService(Context.ALARM_SERVICE)
as AlarmManager;
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
alarmLength, alarmIntent);
}
}
public class Plugin
: IMvxPlugin
{
private _alarmLength = **Your Value Here**;
public void Configure(IMvxPluginConfiguration configuration)
{
if (configuration == null)
return;
var droidConfiguration = (DroidAlarmConfiguration)configuration;
_alarmLength = droidConfiguration.AlarmLength;
}
public void Load()
{
var instance = new DroidAlarm();
instance.AlarmLength = _AlarmLength;
Mvx.RegisterSingleton<IAlarm>(instance);
}
}
Setup.cs - To set the values in one core place for all android/ios/windows
protected override IMvxPluginConfiguration GetPluginConfiguration(Type plugin)
{
if (plugin == typeof(Yours.Alarm.Droid.Plugin))
{
return new Yours.Alarm.Droid.DroidAlarmConfiguration()
{
AlarmLength = **YOUR VALUE HERE**
};
}
return null;
}
You would then follow the same Droid step for iOS and Windows Phone. I hope this helps!

Resources