Xamarin, no show images in the device but only in the simulator

In my project I have some images to display. The funny thing is I can see images in the simulator but not in a real device.

Simulator

Simulator-Screen Shot

iPhone (real device)

iPhone-Screenshot

Solution

iPhone is case-sensitive and the name of your images must be specified correctly. iOS Simulator probably is not case-sensitive and then it displays images with every name.

Xamarin Forms and FingerPrint (iOS and Android only)

In this post I explain how to use Finger Print in your app. This app is working only in iOS or Android.

Add NuGet

In your solution, you have to add a package called Plugin.Fingerprint.

XamarinForms Fingerprint Nuget

Only for Android, in your Droid project add another package called Plugin.CurrentActivity. Also, you have to add a permission for fingerprint (and one specific for Samsung).

<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission 
       android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />

Implementation

Now we implement in our PCL project the request for fingerprint. First of all we have to check if a device supports fingerprints. If yes, we show the typical dialog.

XamarinForms Finferprint Dialog

Now I added a button in the XAML and its name is buttonFingerPrint. For it in the code behind, I added a Clicked event. Then In the new function, I'm checking is the device supports fingerprint.

var result = await CrossFingerprint.Current.IsAvailableAsync(true);

The function IsAvailableAsync as true as parameter but it is useful only for iOS to have a code in return. If the device supports fingerprint, I'll show a dialog request with this code:

if (result) {
    var auth = await CrossFingerprint.Current
               .AuthenticateAsync("Authenticate access to fingerprint");
    if (auth.Authenticated) {
        await DisplayAlert("Whoooowhooo!", "Authentication is done!", "Ok");
    }
    else {
        await DisplayAlert("Something wrong", 
                           "There is a problem with your finger", "Ok");
    }
}
else {
    await DisplayAlert("Finger print not available", 
                       "You can't use your finger for using this app", "Ok");
}

Testing on an emulator

iOS

If you test your project in an iOS device, be careful to allow to use Touch ID from Hardware menu with Toggle Enrolled State. When you want to test your virtual finger, select Matching Touch.

XamarinForms Fingerprint iOS Emulator

Android

Let's go to the Android emulator. You need to set up fingerprint under Settings. From your Terminal call (5554 is the default port of an Android emulator but it can be different in your computer)

telnet 127.0.0.1 5554

Then you have to copy an auth token from an hidden file called .emulator_console_auth_token in your profile. In the terminal execute

auth <yourcode>

Now open Settings > Security > Fingerprint and follow the instruction. When you have to recognise your finder, come back to the terminal and type

finger touch 1

Then you can continue with the wizard.

Now you can execute your app. When your app ask your fingerprint, come back to the Terminal and type again

finger touch 1

Android is always easy to use! You find my solution on GitHub.

Happy coding!

Xamarin, Android and starting a service at device boot

In my previous post called Xamarin: how to Start an Application at Device Boot in Android, I explained how you can change your Android Xamarin's project to launch your app at the device boot.

It is a good start but my problem was a bit more complex. I want to create a background service to track the geolocation of a device. I googled a lot to find a solution without a great success. Then I started to implement my code with some tests. Finally, I found a good way and I'm sharing with you it.

Create a Broadcast Receiver

What is a Broadcast Receiver

A broadcast receiver is a component that responds to system-wide broadcast announcements. Many broadcasts originate from the system—for example, a broadcast announcing that the screen has turned off, the battery is low, or a picture was captured. Applications can also initiate broadcasts—for example, to let other applications know that some data has been downloaded to the device and is available for them to use. Although broadcast receivers don't display a user interface, they may create a status bar notification to alert the user when a broadcast event occurs. More commonly, though, a broadcast receiver is just a "gateway" to other components and is intended to do a very minimal amount of work.

How to implement a Broadcast Receiver

I show you now two implementations of it:

  • BootReceiver: this receiver is responsible to do something when a device starts. For that, we have to allow the app to read this event. In your AndroidManifest.xml you must add a permission called RECEIVE_BOOT_COMPLETED.
  • AirplaneModeReceiver: this receiver filters only the broadcast for ActionAirplaneModeChanged.

I start to show you the second one because it is more simple.

	[BroadcastReceiver]
    [IntentFilter(new[] { Android.Content.Intent.ActionAirplaneModeChanged })]
	public class AirplaneModeReceiver : BroadcastReceiver
	{
		private static readonly string TAG = typeof(AirplaneModeReceiver).FullName;

		public override void OnReceive(Context context, Intent intent)
		{
			Log.WriteLine(LogPriority.Debug, TAG, "AirplaneModeReceiver OnReceive Mode " + 
                          isAirplaneModeOn(context));
		}

		private static bool isAirplaneModeOn(Context context)
		{
			var airplane = Android.Provider.Settings.Global.GetInt(context.ContentResolver, 
                           Android.Provider.Settings.Global.AirplaneModeOn);
			return airplane != 0;
		}
	}

Although you can find a lot of posts where people said that you have to declare your broadcast in the AndroidManifest, I discover if I add the broadcast in the AndroidManifest, the broadcast won't work.

This is the first broadcast. Basically when a user taps on AirPlaneMode, this broadcast receives the status with isAirplaneModeOn function and writes this change in the logs.

BootReceiver is more interesting. When a device is booting, this broadcast starts a service to track the position. Also, in my code you find TimeService: I use this service to check if the service is starting properly and the notification is working and updating.

    [BroadcastReceiver]
    [IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
    public class BootReceiver : BroadcastReceiver
    {
        private static readonly string TAG = typeof(BootReceiver).FullName;

        public override void OnReceive(Context context, Intent intent)
        {
            Log.WriteLine(LogPriority.Debug, TAG, "BootReceiver OnReceive");

            Intent i = new Intent(context, typeof(GPSService));
            i.AddFlags(ActivityFlags.NewTask);
            context.StartService(i);
            Log.WriteLine(LogPriority.Debug, TAG, 
                          "BootReceiver OnReceive LocationService started");

			i = new Intent(context, typeof(TimerService));
			i.AddFlags(ActivityFlags.NewTask);
			context.StartService(i);
			Log.WriteLine(LogPriority.Debug, TAG, 
                          "BootReceiver OnReceive TimerService started");
		}
    }

The interesting part of this code is when I start a service. Basically, I define a new Intent with the type of service I want to call, then I start the service with the context.

Although you find a lot of posts on line, the right way to define a service in your AndroidManifest is the following:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="pro.wordbank.app.locationtest">
	<uses-sdk android:minSdkVersion="15" />
	<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
	<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
	<uses-permission android:name="android.permission.INTERNET" />
	<application android:label="LocationTest">
		<service android:name=".TimerService" />
        <service android:name=".LocationService" />
	</application>
</manifest>

I have publish a complete solution of that on GitHub. Happy coding!

Xamarin: how to Start an Application at Device Bootup in Android

This tutorial will explain to stat an application while the Android device boot-up. For this, we need to  listen to the BOOT_COMPLETED action and react to it.

BOOT_COMPLETED is a Broadcast Action that is broadcast once, after the system has finished booting. You can listen to this action by creating a BroadcastReceiver that then starts your launch Activity when it receives an intent with the BOOT_COMPLETED action.

Add this permission to your manifest

In your Android.Manifest you must add thi permission:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

 

BackgroundTest-AndroidManifest

Then open this file and add the following rows under Application:

    <receiver android:name=".BootReceiver" android:enabled="true" 
              android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
    </receiver>

In this example, we will create a new BroadcastReceiver called BootReceiver.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace BackgroundTest.Droid.BackgroundServices
{
    public class BootReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            Intent i = new Intent(context, typeof(MainActivity));
            i.AddFlags(ActivityFlags.NewTask);
            context.StartActivity(i);
        }
    }
}

Install the application, and then restart the device. You can see the application will start after the device restarts. An implementation of background services starting with Activity, it explains here.

Source code on GitHub.

Happy coding!

Device name in Xamarin

Using Device Information Plugin for Xamarin and Windows, you have access to same information for a device:

  • GenerateAppId: used to generate a unique Id for your app.
  • Id: this is the device specific Id
  • Device Model: get the model of the device
  • Version: get the version of the Operating System

If you want the device name, it's not in this list. Then for that we can create our functions.

IDevice interface

First of all we have to create an interface, I call it IDevice.

using System;
namespace myApp.Interfaces
{
    public interface IDevice
    {
        string DeviceName();
    }
}

The GetDeviceName() method returns the name of the device. You will need to implement this method in iOS and Android.

iOS dependency

Then in iOS project, I add a new dependence like:

using System;
using myApp.iOS;
using myApp.Interfaces;

[assembly: Xamarin.Forms.Dependency(typeof(Device_iOS))]
namespace myApp.iOS
{
    public class Device_iOS: IDevice
    {
        public string GetDeviceName() {
            return UIKit.UIDevice.CurrentDevice.Name;
        }
    }
}

To get the name of the device in iOS, you use the UIDevice class in the UIKit Framework.

Android

For Android, I add the following code to the project:

using System;
using Android.Bluetooth;
using myApp.Droid;
using myApp.Interfaces;
using Xamarin.Forms;

[assembly: Dependency(typeof(Device_Droid))]
namespace myApp.Droid.Dependencies
{
    public class Device_Droid : IDevice
    {
        /// 
        /// Get device the name.
        /// 
        /// The name.
        public string DeviceName()
        {
			BluetoothAdapter myDevice = 
                BluetoothAdapter.DefaultAdapter;
			return myDevice.Name;
        }
    }
}

Getting the device name is a little tricky in Android, which has no public API for that purpose. You can get it from the BluetoothAdapter class; however, to use the BluetoothAdapter class, you need to add the Bluetooth permission in the Android project.

Android Bluetooth

Call dependency

After that I have to add in my main project a call to the dependency.

var platform = DependencyService.Get<IDevice> ();
string name = platform.GetDeviceName();

Happy coding!

A Simple Speedtest Application for Xamarin

In my previous post I described how to check your connection in a C# project. I should have the same function in a PCL project for Xamarin. Based on my project, I created this function:
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace PSC.Xamarin.Connection
{
	/// 
	/// Speed test.
	/// 
	public class SpeedTest
	{
		public double SecondsForOneMb { get; set; } = 0;
		public double KbSeconds { get; set; } = 0;

		/// 
		/// Starts the test to download a file from an url.
		/// Read SecondsForOneMb and KbSeconds for the result
		/// 
		public async Task StartTest()
		{
			string url = 
            "http://puresourcecode.com/file.axd?file=/SpeedTest/1024kb.txt";
			HttpClient client = new HttpClient();

			// get current tickcount 
			double starttime = Environment.TickCount;

			// download file from the specified URL, 
			// and save it to C:\speedtest.txt
			// in your project change the path of the following line
			var httpResponse = await client.GetAsync(url);
			byte[] dataBuffer = 
                   await httpResponse.Content.ReadAsByteArrayAsync();

			// get current tickcount
			double endtime = Environment.TickCount;

			// how many seconds did it take?
			// we are calculating this by subtracting starttime from
			// endtime and dividing by 1000 (since the tickcount is in 
			// miliseconds 1000 ms = 1 sec)
			SecondsForOneMb = Math.Floor(endtime - starttime) / 1000;

			// calculate download rate in kb per sec.
			// this is done by dividing 1024 by the number of seconds it
			// took to download the file (1024 bytes = 1 kilobyte)
			KbSeconds = Math.Round(1024 / SecondsForOneMb);
		}
	}
}

Happy coding!

Xamarin forms, UWP Windows 10 App, TitleBar and Status bar customization

Customize the title bar of your Universal App for Windows 10 is quite easy, but you need to write different code for PC and Mobile. The class that allows you to customize the title bar:

  • when running on a PC is called TitleBar
  • when running on a Mobile is called StatusBar

Before to call the API you first need to check if it exists (true if you are running on that platform):

//PC customization
if (ApiInformation.IsTypePresent(
                   "Windows.UI.ViewManagement.ApplicationView"))
{
    var titleBar = ApplicationView.GetForCurrentView().TitleBar;
    if (titleBar != null)
    {
        titleBar.ButtonBackgroundColor = Colors.DarkBlue;
        titleBar.ButtonForegroundColor = Colors.White;
        titleBar.BackgroundColor = Colors.Blue;
        titleBar.ForegroundColor = Colors.White;
    }
 }

//Mobile customization
if (ApiInformation.IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{

    var statusBar = StatusBar.GetForCurrentView();
    if (statusBar != null)
    {
        statusBar.BackgroundOpacity = 1;
        statusBar.BackgroundColor = Colors.DarkBlue;
        statusBar.ForegroundColor = Colors.White;
    }
}

You could receive an error

StatusBar doesn't exist in context

You resolve it to add in the reference for UWP project Windows Mobile Extensions for the UWP and Windows Desktop Extensions for the UWP.

Extensions

Happy coding!

Add Calabash to your Xamarin project

Why add Calabash to your project? Because without it you can't test your application with Xamarin Test Cloud.

Scenario

I created my app for iOS and I want to test it on Xamarin Cloud. First thing I have to install to create a UITest easily is Xamarin Test Recorder. When I opened this application, I was confuse because nobody explain what kind of app I can choose. Basically if you have a iOS project, before use Xamarin Test Recorder, you must create an .ipa and it means you have to create an archive and publish it.

After createing a UITest project you can sent it with Export function directly to TestCloud. The problem I discovered is

Errors and Failures:
1) SetUp Error : RecorderTest.NewTest
   SetUp : System.Exception : Unable to contact test backend running in app. A common cause is that the app is not properly linked with Calabash. Please verify that it includes the Calabash component.
  at Xamarin.UITest.iOS.iOSAppLauncher.EnsureCalabashRunning (ICalabashConnection connection) <0x7507b30 + 0x0005f> in <filename unknown>:0 
  at Xamarin.UITest.iOS.iOSAppLauncher.LaunchApp (IiOSAppConfiguration appConfiguration, Xamarin.UITest.Shared.Http.HttpClient httpClient, Xamarin.UITest.TestCloud.TestCloudiOSAppConfiguration testCloudAppConfiguration, Xamarin.UITest.Shared.Http.HttpClient testCloudWsClient, Xamarin.UITest.Shared.Http.HttpClient xtcServicesClient, Boolean testCloudUseDeviceAgent) <0x6cbb340 + 0x0019b> in <filename unknown>:0 
  at Xamarin.UITest.iOS.iOSApp..ctor (IiOSAppConfiguration appConfiguration, IExecutor executor) <0x6b39778 + 0x00b03> in <filename unknown>:0 
  at Xamarin.UITest.iOS.iOSApp..ctor (IiOSAppConfiguration appConfiguration) <0x6b39738 + 0x0001f> in <filename unknown>:0 
  at Xamarin.UITest.Configuration.iOSAppConfigurator.StartApp (AppDataMode appDataMode) <0x6b38e18 + 0x00063> in <filename unknown>:0 
  at RecorderTest.SetUp () <0x6b37f70 + 0x0004f> in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) <0x31e7110 + 0x00093> in <filename unknown>:0 

Why this? Because I didn't install Calabash.

Add Calabash for iOS

First al all, open your Terminal with admin privileges. Then execute this script:

gem install calabash-cucumber gem install calabash-android gem install xamarin-test-cloud

Now in your system there are the basic for Calabash.

Now you can install a Calabash sandbox. Do not use sudo to install the Calabash Sandbox. Execute this script

curl -sSL https://raw.githubusercontent.com/calabash/install/master/install-osx.sh | bash

Now in the directory of your iOS project execute this script:

calabash-ios download

And finally you can generate a feature folder. The features folder is a special folder where Cucumber expects the test code to reside. Calabash can create this folder and provide some boilerplate code to get us started.

The last step is to execute in your iOS project the following command in the Terminal

calabash-ios gen

Now you create a new .ipa and do a new test. This time it will work!

Happy coding!

An error occurs with MobileCenter for Xamarin iOS

I added Microsoft Mobile Center to my project after creating the app there. On MobileCenter documentation you can know the Install Identifier for your application (MobileCenter documentation is here).

System.Guid installId = MobileCenter.InstallId;

This function is working fine if you have Android or iOS 10. With iOS less than 10 an error occurs:

Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

and the StackTrace is similar to

at System.Guid+GuidResult.SetFailure (System.Guid+ParseFailureKind failure, System.String failureMessageID, System.Object failureMessageFormatArgument, System.String failureArgumentName, System.Exception innerException) [0x00030] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:198 \n at System.Guid+GuidResult.SetFailure (System.Guid+ParseFailureKind failure, System.String failureMessageID) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:184 \n at System.Guid.TryParseGuidWithDashes (System.String guidString, System.Guid+GuidResult& result) [0x0008f] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:695 \n at System.Guid.TryParseGuid (System.String g, System.Guid+GuidStyles flags, System.Guid+GuidResult& result) [0x00115] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:443 \n at System.Guid.Parse (System.String input) [0x00021] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:262 \n at Microsoft.Azure.Mobile.MobileCenter.get_InstallId () [0x0000a] in :0 \n at myInventories.Helpers.LogHelpers.SendMessageToAzure (System.String EventName, System.String PageName, System.String BaseClass, myInventories.Logs.ActionType Action, System.String MoreInfo, System.Collections.Generic.Dictionary`2[TKey,TValue] DictionaryInfo) [0x00008] in /Users/enricorossini/Projects/myInventories/myInventories/myInventories/Helpers/LogHelpers.cs:72

To avoid this error you have to check the OS version.

Implementation

In your solution you have to add in all projects Device Information Plugin (for NuGet Xam.Plugin.DeviceInfo). Then you check easily the OS version with

// check the OS version to avoid error on MobileCenter
string MobileId = "";
var osInfo = CrossDeviceInfo.Current;
if ((osInfo.Platform == Plugin.DeviceInfo.Abstractions.Platform.iOS) && 
    (osInfo.VersionNumber.Major < 10)) 
{
    MobileId = "Unknown";
}
else {
    MobileId = MobileCenter.InstallId.ToString();
}

Happy coding!

Xamarin.Forms (Android): Workaround For Splash Screen With Logo

What is the quickest way to add a splash screen to your Android project in Xamarin?

  1. Create a new folder under the Android project folder "Resources" with the name "Values".
  2. In that folder add a new "Styles.xml" file. Add -> New Item, then XML File. Make sure after creating this file to have the property "Build Action" set to the default value: "AndroidResource". In this file we will create a new theme "Theme.Splash" that set the background of our Splash with our logo.
  3. Edit the file to be as follows:
    <?xml version="1.0" encoding="utf-8" ?>
    <resources>
      <style name="Theme.Splash" parent="android:Theme">
        <item name="android:windowBackground">@drawable/myLogo</item>
        <item name="android:windowNoTitle">true</item>
      </style>
    </resources>
  4. Add a Splash Activity to the Android Project using Add -> New Item, then Activity. Let's name it SplashActivity.
  5. Change the attributes of the activity to set the theme attribute to "@styles/Theme.Splash", MainLauncher attribute to "True", and NoHistory also to "True". The Theme attribute is responsible for the style of the activity, the MainLauncher is responsible for making this activity the first activity to show and NoHistory is responsible for preventing navigation back to this activity. The activity attributes should look like the following:
    [Activity(Theme = "@style/Theme.Splash", 
              MainLauncher = true, 
              NoHistory = true)]
  6. Add the following code to the "OnCreate" method in Splash activity immediately after the comments // Create your application here:
    // Simulate a long loading process on app
    System.Threading.Thread.Sleep(10000);
    StartActivity(typeof(MainActivity));

    The first line simulates a long-loading process.
    The second line Start our MainActivity where our application will run main tasks after the Splash is shown.

  7. Just before running the application, we need to ensure that the MainLancher attribute of the MainActivity is set to false:
    [Activity(Label = "HandyApp.Droid", Icon = "@drawable/icon", 
              MainLauncher = false, 
              ConfigurationChanges = ConfigChanges.ScreenSize | 
                                     ConfigChanges.Orientation)]
  8. And let's run the application.

Do you want to logo in the center of your screen?

  1. In Styles.xml, remove the entire line where the "windowBackground" attribute and value are. Instead insert the following line specifying that the background is of White color:
    <item name="android:colorBackground">@android:color/white</item> 
  2. In the Resources folder, create a new folder "layout". Here we will add our layout for the SplashActivity.
  3. In the layout folder, add a new Android Layout: Add->New Item...->Android Layout. let's call it "SplashLayout.xaml".
  4. Edit the source of SplashLayout to match the following markup:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:minHeight="25px"
        android:minWidth="25px"
        android:gravity="center"
        android:background="#6d6763">
        <ImageView
            android:src="@drawable/myLogo"
            android:layout_height="fill_parent"
            android:layout_width="wrap_content"
            android:layout_gravity="center"
            android:id="@+id/imageView1" />
    </LinearLayout>
    Here we have an ImageView in the center with source (src) of your logo in the drawable folder.
  5. In SplashActivity.cs, edit the "OnCreate" method to match the following code:
    [Activity(Theme = "@style/Theme.Splash", 
              MainLauncher = true, NoHistory = true)]
    public class SplashActivity : Activity {
        protected override void OnCreate(Bundle savedInstanceState) {
            base.OnCreate(savedInstanceState);
    
            SetContentView(Resource.Layout.SplashLayout);
            System.Threading.ThreadPool
                            .QueueUserWorkItem(o => LoadActivity());
        }
    }
    In this code, we set the content view of the SplashActivity to our SplashLayout.xaml and then we queue the MainActivity in the ThreadPool object using the LoadActivity method that we will write in the next step.
  6. Add a new method to the SplashActivity class called "LoadActivity":
    private void LoadActivity()     {
        // Simulate a long pause
        System.Threading.Thread.Sleep(5000);
        RunOnUiThread(() => StartActivity(typeof(MainActivity)));
    }  
    Note how we shifted the sleep period to this method.
  7. Run the application

Advertsing

125X125_06

Planet Xamarin

Planet Xamarin

Calendar

<<  September 2017  >>
MonTueWedThuFriSatSun
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar

Month List