Tag Archives: c#

JonikaBot – Möt min Twitterbot

Lite då och får man lust att hitta på något kul projekt hemma, något annorlunda än det man gör dagligen på jobbet. Men ofta är det väldigt svårt att komma på bra idéer, och utan bra idéer blir det ofta tråkigt att lägga tid på saker.

Men plötsligt kom jag på vad jag ville bygga, en Twitterbot! Det dök genast upp den hel del idéer på vad den ska göra (men jag avslöjar bara dom som är klara just nu 🙂 ). Min första idé var en “Smurfalizer”. Det har ju funnits en som försmurfar webbsidor i flera år, så varför inte göra det samma med tweets? Just nu används en ordlista med knappt 400 verb. Det är dessutom viss slump i mina beräkningar, så inte alla verb översätts. Om någon vill testa den, se till att ha gott om verb i texten. Tagga en tweet med #jonikabotsmurf, så bör det komma ett svar inom ca 3 minuter. Så här kan ett svar se ut:

Den andra idén var att kombinera mitt intresse för programmering med mitt intresse för hockey, och i synnerhet Färjestad.  Jag har en hel del idéer inom ämnet, men den första funktionen är att ta den tweetande användarens profilbild i storleken 400x400px, lägga på ett Färjestademblem i nedre vänstra hörnet och skicka tillbaka bilden i en tweet. Om någon känner ett behov av en snyggare profilbild så skicka en tweet med vilket innehåll som helst och taga den med #jonikabotfbk

Boten kommer säkert byggas på med funktioner, så stay tuned 🙂

När boten väl var i hyfsat skick insåg jag nästa bekymmer: Var ska jag köra den? Tiden då hemmet kryllar av servrar och datorer är förbi, och nu för tiden är det mest paddor och telefoner i huset, förutom iMac:en (som jag för övrigt utvecklar på hemma). En tur till NetOnNet gjorde mig en Raspberry Pi 3 rikare, och efter 1 knapp timma var Raspbian Jessie Lite installerat, och burken uppkopplad på Wifi. Eftersom jag lever med C# så föll valet på Mono, vilket var lätt att installera även på Raspberry.

OBS! Boten är högst experimentell och innehåller säker en del buggar, så kommer det inget svar kan den antingen vara nere för tillfället, eller så har du lyckats hitta en bugg 🙂

Så här ser den extremt snygga konsolen ut:

Xamarin Droid: Putting LocationManager in a separate class

I almost always put all logic in som sort of separate classes in my projects. I simply think it looks better, is way more easy to understand and the better way.

When using Xamarin to create cross-plattform apps, you probably use dependency injection to minimize ugly code blocks like this.

if (Device.OS == TargetPlatform.iOS) {
}else{
}

That also means you pretty much need to create interfaces that you can use for all plattforms, which leads to this topic: You need to put logic in classes.

I had a bit of a struggle to implement location services on Android this way, hence this post. Most bloggs and articles i found used the main activity. For iOS it was very straight forward, so I’m leaving that part out.

And to be clear: This is my POC-code, not intended to copy and paste into production!

First of all, i had to create an interface i could use for iOS and Droid. I kept it simple, and only included stuff i needed: The current location, a method to initialize everything and a event to trigger when position changes.

public interface ILocationManager
{
	Tuple<double, double> GetCurrentLocation();
	void StartLocationUpdates();
	event EventHandler LocationUpdated;
}

I also created i class holding the main activity context. I needed this to be able to use the context from my class.

public class Config {
	public static MainActivity context = null;
}

In my MainActivity i set the context of the class above:

Config.context = this;

From my cross-plattform code i need to get a instance of ILocationManager for the current plattform. For this, i use the DependencyService. I Also call my StartLocationUpdates method to start getting location updates:

private ILocationManager LocManager = DependencyService.Get<ILocationManager> ();
LocManager.StartLocationUpdates ();

So far, all code except the config class is the same for both iOS and Android. But this is where i hit the wall on Android. I spent days trying to figure out how to create a class using my interface that would work in the same way my iOS code did. This is my solution

First, make sure to put this line above the namespace in your class to get dependency injection to work:

[assembly: Xamarin.Forms.Dependency (typeof(NautiCalc.Droid.LocationManager))]

The second thing i found important was to decorate your class with the Activity attribute. This makes sure your activity is inserted into the Manifest, which is important. You also need to inherit Activity class, and implement your interface.

[Activity]
public class LocationManager : Activity, NautiCalc.ILocationManager, ILocationListener

The rest is kind of straight forward. You need to implement a few methods because of the inheritance of Activity, and of course your own interface.

Here is my complete code:

using System;
using Android.Content;
using Android.App;
using Android.Locations;
using Android.OS;
using Android.Widget;

[assembly: Xamarin.Forms.Dependency (typeof(NautiCalc.Droid.LocationManager))]
namespace NautiCalc.Droid
{
	[Activity]
	public class LocationManager : Activity, NautiCalc.ILocationManager, ILocationListener
	{
		// event for the location changing
		public event EventHandler LocationUpdated = delegate {};

		protected Android.Locations.LocationManager locMgr;

		public Android.Locations.LocationManager LocMgr {
			get { return this.locMgr; }
		}

		public Tuple<double, double> GetCurrentLocation ()
		{
			Location lastKnown = locMgr.GetLastKnownLocation (Android.Locations.LocationManager.NetworkProvider);
			if (lastKnown != null) {
				return Tuple.Create (lastKnown.Latitude, lastKnown.Longitude);
			}
			return null;
		}

		public void StartLocationUpdates ()
		{
			locMgr = (Android.Locations.LocationManager)Config.context.GetSystemService (Context.LocationService);

			if (locMgr.AllProviders.Contains (Android.Locations.LocationManager.GpsProvider) && locMgr.IsProviderEnabled (Android.Locations.LocationManager.GpsProvider)) {
				locMgr.RequestLocationUpdates (Android.Locations.LocationManager.GpsProvider, 2000, 1, this);
			} 
		}

		protected override void OnPause ()
		{
			base.OnPause ();
			locMgr.RemoveUpdates (this);
		}

		public void OnProviderEnabled (string provider)
		{
		}

		public void OnProviderDisabled (string provider)
		{
		}

		public void OnStatusChanged (string provider, Availability status, Bundle extras)
		{
		}

		public void OnLocationChanged (Location location)
		{
			LocationData CurrentLocation = new LocationData ();
			CurrentLocation.Altitude = location.Altitude;
			CurrentLocation.Course = (double)location.Bearing;
			CurrentLocation.Latitude = location.Latitude;
			CurrentLocation.Longitude = location.Longitude;
			CurrentLocation.Speed = (double)location.Speed;
			LocationUpdated (this, new LocationUpdatedEventArgs (CurrentLocation));
		}
	}
}

Not that hard, once you figured it out. Happy coding!

Am i the only one who rewrites the same project over and over again? :)

Am I the only one why keeps rewriting the same code, over and over again, while learning new languages or plattforms?

For me, it’s mainly because i hate to run Hello world examples or create meaningless projects. Because of that, my hobby project NautiCalc (you can find i web version right here) has been rewritten in PHP, Objective-C, Java, Javascript  on plattforms like iOS, Android, Web and OS X.

And now, it’s time again! Last week I attended to DevSum 2016, and I got totally hooked by Xamarin! Time to build a cross-plattform version, running on iOS and Android!

After 3-4 days of coding with Xamarin on my Mac, my first reactions are:

  • Xamarin studio is surprisingly  complete, and supports pretty much everything you need!
  • Creating UI in XAML is really nice! I’ve missed it since i dropped Silverlight.
  • Xamarin.Forms is really nice and makes it easy to write cross-plattform apps. The best thing is that it maps to native components for each plattform.
  • The built-in dependency injection adds to that!

NautiCalc in Xamarin

So, my main screen is built up by a TabbedPage built with XAML. In this page all i do is adding my namespace, so i can reference separate pages for each tab, instead of have one huge XAML file.

<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:me="clr-namespace:NautiCalc;assembly=NautiCalc" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="NautiCalc.TabbedMainPage">
	<TabbedPage.Children>
		<me:NavigatePage />
		<me:WeatherPage />
		<me:CalculatePage />
		<me:AboutPage />
	</TabbedPage.Children>
</TabbedPage>

The TabbedPage render native navigation menus for each plattform. IT works with Windows as well, but who cares about Windows phones?

Below you can see the about page on Android and iOS, where the navigation is created by the XAML in my TabbedPage. The Android version lacks graphics at this moment,

AndroidAboutScreen

iOSAboutScreen

The XAML for the about page above is also the same on both plattforms. There’s only one thing thats different on each plattform, and that is the margins:

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage Icon="Nauticalc_24.png" Title="AboutPage" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="NautiCalc.AboutPage">
	<ContentPage.Padding>
		<OnPlatform x:TypeArguments="Thickness">
			<OnPlatform.iOS>
				15,120,5,0
			</OnPlatform.iOS>
			<OnPlatform.Android>
				15,100,5,0
			</OnPlatform.Android>
		</OnPlatform>
	</ContentPage.Padding>
	<StackLayout>
		<StackLayout Padding="15,0,15,0">
			<Image Source="Nauticalc_72.png" />
			<Label FontSize="24" Margin="0,25,0,25" FontAttributes="Bold" Text="NautiCalc" HorizontalOptions="Center" VerticalOptions="Center" />
			<Label Font="Small" Text="Helps you navigate at sea. Keeps track at your speed, course and position and helps you calculate how fast you need to go, how far you can reach in a certain time and what distance you cover at a certain speed and time" />
		</StackLayout>
		<StackLayout Padding="15,50,15,0" Orientation="Horizontal">
			<Label Text="Twitter:" VerticalOptions="Center" />
			<Label Text="@jonlin76" TextColor="Blue" x:Name="JonikaTwitter" VerticalOptions="Center" HorizontalOptions="EndAndExpand" />
		</StackLayout>
		<StackLayout Padding="15,0,15,0" Orientation="Horizontal">
			<Label Text="E-mail:" VerticalOptions="Center" />
			<Label Text="jonas@jonika.nu" TextColor="Blue" x:Name="JonikaMail" VerticalOptions="Center" HorizontalOptions="EndAndExpand" />
		</StackLayout>
		<StackLayout Padding="15,0,15,0" Orientation="Horizontal">
			<Label Text="Blogg:" VerticalOptions="Center" />
			<Label Text="http://jonika.nu/JonasBlogg/" x:Name="JonikaBlogg" TextColor="Blue" VerticalOptions="Center" HorizontalOptions="EndAndExpand" />
		</StackLayout>
	</StackLayout>
</ContentPage>

And, off course, there’s a small code behind as well. We need to add a GestureRecognizer to enable click on the hyperlinks:

		public AboutPage ()
		{
			InitializeComponent ();

			TapGestureRecognizer tapRec = new TapGestureRecognizer ();
			tapRec.Tapped += (object sender, EventArgs e) => {
				if (sender.Equals (JonikaTwitter))
					Device.OpenUri (new Uri ("https://twitter.com/jonlin76"));
				if (sender.Equals (JonikaMail))
					Device.OpenUri (new Uri ("mailto:jonas@jonika.nu"));
				if (sender.Equals (JonikaBlogg))
					Device.OpenUri (new Uri ("http://www.jonika.nu/JonasBlogg"));
			};

			JonikaBlogg.GestureRecognizers.Add (tapRec);
			JonikaMail.GestureRecognizers.Add (tapRec);
			JonikaTwitter.GestureRecognizers.Add (tapRec);
		}

My NavigationPage (the main screen in the app) uses Xamarin.Forms.Maps. This package has to be installed from NuGet, and it also requires some initiation and for Android som configuration like API key. When that is done, map component renders Apple Maps on iOS, Google Maps on Android and Bing Maps on Windows. All with the same code!

iOSNavigationScreen

AndroidNavigationPage

Cross-plattform have never been easier, and ism deeply impressed by both Xamarin Studio and the framework!

Time will tell if the app finishes, or maybe i’ll change my mind and rewrites this project before it gets done. Who knows 🙂

Weather-API 0.2 available

Changes in this version:

  • Added method <GetForecast(DateTime) to support fetching data for a certain time. Return null if no data available for time.
  • Renamed class <SMHI> to <Weather> to make it a bit more flexible
  • Renamed class <TimeSeriesData> to <TimeSeries>

Read more here

NautiCalc v0.5.61

En del mindre ändringar:

  • Kartan fungerar nu på Windows Phone
  • Fält som visar när positionen senast uppdaterades
  • Ikoner när appen laddas ner, fungerar för Android och iOS
  • På iOS ser appen nu ut som en “native”-app. Safaris gränssnintt döljs.

Appen finns här: http://www.jonika.nu/NautiCalc/