MvvmCross plugin for setting up Alarms - xamarin

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!

Related

How to use an interface

I'm trying to build my first xamarin app, which I'm building using forms. One of the features of the app is sending users locations and have to do that even if the app is in the background. So I came across James Montemagno's GeolocatorPlugin, which promised to do just that.
As the documentation was not that clear on how to implement his plugin in the background I looked through the projects closed issues and found a guy which gave an example of a simple case of using the plugin with a service. (https://github.com/jamesmontemagno/GeolocatorPlugin/issues/272)
I've adopted the code and created the service. The service are using an interface to start the service and now my problem is how to make use of the interface to make the service run.
In my shared project I put the interface and the viewmodel and in xamarin.android project I put the service.
The interface - IGeolocationBackgroundService:
public interface IGeolocationBackgroundService {
void StartService();
void StartTracking();
}
The viewmodel - GeolocatorPageViewModel:
public class GeolocatorPageViewModel
{
public Position _currentUserPosition { get; set; }
public string CoordinatesString { get; set; }
public List<string> userPositions { get; set; }
public ICommand StartTrackingCommand => new Command(async () =>
{
if (CrossGeolocator.Current.IsListening)
{
await CrossGeolocator.Current.StopListeningAsync();
}
CrossGeolocator.Current.DesiredAccuracy = 25;
CrossGeolocator.Current.PositionChanged += Geolocator_PositionChanged;
await CrossGeolocator.Current.StartListeningAsync(
TimeSpan.FromSeconds(3), 5);
});
private void Geolocator_PositionChanged(object sender, PositionEventArgs e)
{
var position = e.Position;
_currentUserPosition = position;
var positionString = $"Latitude: {position.Latitude}, Longitude: {position.Longitude}";
CoordinatesString = positionString;
Device.BeginInvokeOnMainThread(() => CoordinatesString = positionString);
userPositions.Add(positionString);
Debug.WriteLine($"Position changed event. User position: {CoordinatesString}");
}
}
The service - GeolocationService:
[assembly: Xamarin.Forms.Dependency(typeof(GeolocationService))]
namespace MyApp.Droid.Services
{
[Service]
public class GeolocationService : Service, IGeolocationBackgroundService
{
Context context;
private static readonly string CHANNEL_ID = "geolocationServiceChannel";
public GeolocatorPageViewModel ViewModel { get; private set; }
public override IBinder OnBind(Intent intent)
{
return null;
}
public GeolocationService(Context context)
{
this.context = context;
CreateNotificationChannel();
}
private void CreateNotificationChannel()
{
NotificationChannel serviceChannel = new NotificationChannel(CHANNEL_ID,
"GeolocationService", Android.App.NotificationImportance.Default);
NotificationManager manager = context.GetSystemService(Context.NotificationService) as NotificationManager;
manager.CreateNotificationChannel(serviceChannel);
}
//[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
var newIntent = new Intent(this, typeof(MainActivity));
newIntent.AddFlags(ActivityFlags.ClearTop);
newIntent.AddFlags(ActivityFlags.SingleTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, newIntent, 0);
var builder = new Notification.Builder(this, CHANNEL_ID);
var notification = builder.SetContentIntent(pendingIntent)
.SetSmallIcon(Resource.Drawable.ic_media_play_light)
.SetAutoCancel(false)
.SetTicker("Locator is recording")
.SetContentTitle("GeolocationService")
.SetContentText("Geolocator is recording for position changes.")
.Build();
StartForeground(112, notification);
//ViewModel = new GeolocatorPageViewModel();
return StartCommandResult.Sticky;
}
public void StartService()
=> context.StartService(new Intent(context, typeof(GeolocationService)));
public void StartTracking()
{
ViewModel = new GeolocatorPageViewModel();
ViewModel.StartTrackingCommand.Execute(null);
}
}
}
So be clear, I need to start the service and I'm not used to interfaces, so how do I call the interface?
use DependencyService to get a reference to your service and then start it
var svc = DependencyService.Get<IGeolocationBackgroundService>();
svc.StartService();
svc.StartTracking();

How to play a system sound in Xamarin.Forms

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/

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.

Session not saved in ServiceStack

I want to use the session feature but without athentication.
I already added Plugins.Add(new SessionFeature()) to AppHost.cs and I have the following code
public class CustomService : Service
{
public CustomResponse Any(CustomRequest pRequest)
{
var CustomSession = base.Session.Get<CustomType>("MySession");//try to get the session
if (CustomSession == null)
{
//create a new session
CustomSession = new CustomType{MyId=1};
base.Session["MySession"] = CustomSession;
//base.Session.Set("MySession", CustomSession); //also tried this, to save the session.
this.SaveSession(CustomSession, new TimeSpan (0,20,0)); //Save the Session
}
}
}
The problem I'm having is that base.Session.Get<CustomType>("MySession") is always null.
Am I missing something on the implementation of sessions?
You will need to save your session using base.SaveSession(). See here near the bottom there is a section title 'Saving in Service'.
public class MyAppHost : AppHostBase
{
public MyAppHost() : base("MyService", typeof(CustomService).Assembly)
{
}
public override void Configure(Container container)
{
Plugins.Add(new SessionFeature());
}
}
public class CustomType : AuthUserSession
{
public int MyId { get; set; }
}
[Route("/CustomPath")]
public class CustomRequest
{
}
public class CustomResponse
{
}
public class CustomService : Service
{
public CustomResponse Any(CustomRequest pRequest)
{
var CustomSession = base.SessionAs<CustomType>();
if (CustomSession.MyId == 0)
{
CustomSession.MyId = 1;
this.SaveSession(CustomSession, new TimeSpan(0,20,0));
}
return new CustomResponse();
}
}
Update:
There is a Resharper issue with extension methods, see here, which seems to affect SaveSession().
Work Arounds:
ServiceExtensions.SaveSession(this, CustomSession); ReSharper may prompt to reformat and it will work.
Ctrl-Alt-space to reformat
RequestContext.Get<IHttpRequest>().SaveSession(CustomSession) can save the
session.

WP7 Mock Microsoft.Devices.Sensors.Compass when using the emulator

I'd like to be able to simulate the compass sensor when running a Windows Phone 7.1 in the emulator.
At this stage I don't particularly care what data the compass returns. Just that I can run against something when using the emulator to test the code in question.
I'm aware that I could deploy to my dev unlocked phone to test compass functionality but I've found the connection via the Zune software to drop out frequently.
Update
I've looked into creating my own wrapper class that could simulate the compass when running a debug build and the compass isn't otherwise supported.
The Microsoft.Devices.Sensors.CompassReading struct has me a bit stumpted. Because it is a struct where the properties can only be set internally I can't inherit from it to provide my own values back. I looked at using reflection to brute force some values in but Silverlight doesn't appear to allow it.
as you already noticed I had a similar problem. when I mocked the compass sensor, I also had difficulties because you cannot inherite from the existing classes and write your own logic. Therefore I wrote my own compass interface which is the only compass functionality used by my application. Then there are two implementations, one wrapper to the WP7 compass functionalities and my mock compass.
I can show you some code, but not before weekend as I'm not at my delevopment machine atm.
Edit:
You already got it but for other people who have the same problem I'll add my code. As I already said, I wrote an interface and two implementations, one for the phone and a mock implementation.
Compass Interface
public interface ICompass
{
#region Methods
void Start();
void Stop();
#endregion
#region Properties
CompassData CurrentValue { get; }
bool IsDataValid { get; }
TimeSpan TimeBetweenUpdates { get; set; }
#endregion
#region Events
event EventHandler<CalibrationEventArgs> Calibrate;
event EventHandler<CompassDataChangedEventArgs> CurrentValueChanged;
#endregion
}
Used data classes and event args
public class CompassData
{
public CompassData(double headingAccurancy, double magneticHeading, Vector3 magnetometerReading, DateTimeOffset timestamp, double trueHeading)
{
HeadingAccuracy = headingAccurancy;
MagneticHeading = magneticHeading;
MagnetometerReading = magnetometerReading;
Timestamp = timestamp;
TrueHeading = trueHeading;
}
public CompassData(CompassReading compassReading)
{
HeadingAccuracy = compassReading.HeadingAccuracy;
MagneticHeading = compassReading.MagneticHeading;
MagnetometerReading = compassReading.MagnetometerReading;
Timestamp = compassReading.Timestamp;
TrueHeading = compassReading.TrueHeading;
}
#region Properties
public double HeadingAccuracy { get; private set; }
public double MagneticHeading { get; private set; }
public Vector3 MagnetometerReading { get; private set; }
public DateTimeOffset Timestamp { get; private set; }
public double TrueHeading { get; private set; }
#endregion
}
public class CompassDataChangedEventArgs : EventArgs
{
public CompassDataChangedEventArgs(CompassData compassData)
{
CompassData = compassData;
}
public CompassData CompassData { get; private set; }
}
WP7 implementation
public class DeviceCompass : ICompass
{
private Compass _compass;
#region Implementation of ICompass
public void Start()
{
if(_compass == null)
{
_compass = new Compass {TimeBetweenUpdates = TimeBetweenUpdates};
// get TimeBetweenUpdates because the device could have change it to another value
TimeBetweenUpdates = _compass.TimeBetweenUpdates;
// attach to events
_compass.CurrentValueChanged += CompassCurrentValueChanged;
_compass.Calibrate += CompassCalibrate;
}
_compass.Start();
}
public void Stop()
{
if(_compass != null)
{
_compass.Stop();
}
}
public CompassData CurrentValue
{
get { return _compass != null ? new CompassData(_compass.CurrentValue) : default(CompassData); }
}
public bool IsDataValid
{
get { return _compass != null ? _compass.IsDataValid : false; }
}
public TimeSpan TimeBetweenUpdates { get; set; }
public event EventHandler<CalibrationEventArgs> Calibrate;
public event EventHandler<CompassDataChangedEventArgs> CurrentValueChanged;
#endregion
#region Private methods
private void CompassCalibrate(object sender, CalibrationEventArgs e)
{
EventHandler<CalibrationEventArgs> calibrate = Calibrate;
if (calibrate != null)
{
calibrate(sender, e);
}
}
private void CompassCurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
{
EventHandler<CompassDataChangedEventArgs> currentValueChanged = CurrentValueChanged;
if (currentValueChanged != null)
{
currentValueChanged(sender, new CompassDataChangedEventArgs(new CompassData(e.SensorReading)));
}
}
#endregion
}
Mock implementation
public class MockCompass : ICompass
{
private readonly Timer _timer;
private CompassData _currentValue;
private bool _isDataValid;
private TimeSpan _timeBetweenUpdates;
private bool _isStarted;
private readonly Random _random;
public MockCompass()
{
_random = new Random();
_timer = new Timer(TimerEllapsed, null, Timeout.Infinite, Timeout.Infinite);
_timeBetweenUpdates = new TimeSpan();
_currentValue = new CompassData(0, 0, new Vector3(), new DateTimeOffset(), 0);
}
#region Implementation of ICompass
public void Start()
{
_timer.Change(0, (int)TimeBetweenUpdates.TotalMilliseconds);
_isStarted = true;
}
public void Stop()
{
_isStarted = false;
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_isDataValid = false;
}
public CompassData CurrentValue
{
get { return _currentValue; }
}
public bool IsDataValid
{
get { return _isDataValid; }
}
public TimeSpan TimeBetweenUpdates
{
get { return _timeBetweenUpdates; }
set
{
_timeBetweenUpdates = value;
if (_isStarted)
{
_timer.Change(0, (int) TimeBetweenUpdates.TotalMilliseconds);
}
}
}
public event EventHandler<CalibrationEventArgs> Calibrate;
public event EventHandler<CompassDataChangedEventArgs> CurrentValueChanged;
#endregion
#region Private methods
private void TimerEllapsed(object state)
{
_currentValue = new CompassData(_random.NextDouble()*5,
(_currentValue.MagneticHeading + 0.1)%360,
_currentValue.MagnetometerReading,
new DateTimeOffset(DateTime.UtcNow),
(_currentValue.TrueHeading + 0.1)%360);
_isDataValid = true;
EventHandler<CompassDataChangedEventArgs> currentValueChanged = CurrentValueChanged;
if(currentValueChanged != null)
{
currentValueChanged(this, new CompassDataChangedEventArgs(_currentValue));
}
}
#endregion
}

Resources