Android Wearable: Creating A Simple Survey Feature Using Xamarin

Having the opportunity to work with android wearables, particularly smart watches, is very exciting as this is getting more popular nowadays. Building mobile and wearable device apps is not as complex as you may think. I admit that at first I was kind of afraid when I was assigned to explore and build apps for mobile and wearable, but using the right tools and technologies makes life easier for me to build those apps and prototypes.

Ermm.. right tools?

Yes, and I was referring to the awesome Xamarin. Xamarin allows you to build cross-platform apps for Android, iOS and Windows and it uses C# as the backend language. They also introduced Xamarin Forms which allows you to easily create native UI layouts that can be shared across Android, iOS, and Windows Phone. For as long as you know C# then creating the logic for your app is easy because you are already familiar with the syntax and most of all the .NET libraries. The only learning curve that you will need to take when transitioning from web to mobile is that you will need to know and understand how Android, iOS and Windows platform works and how each framework interpret stuffs. To trim down a bit of that learning curve, I have decided to use Xamarin and Visual Studio for the following reasons:

  • Xamarin is now fully integrated with the latest Visual Studio release (VS 2015 as of this writing).

  • Xamarin allows you to build cross-platform apps (iOS, Andriod and Windows app) using C#.

  • I am an experienced C# developer.

  • I am more familiar with Visual Studio development tools.

  • I don't need to learn how to use other frameworks, editors, tools and other programming languages to build native apps.

  • I can take advantage of the cool features provided by Xamarin such as cloud testing and app monitoring.

  • Xamarin and Visual Studio are quite popular and stable platform for building real world apps.

  • Xamarin have their own dedicated support site. So when you encounter any problem during your development, you can easily post your query to their dedicated forums.

I'm writing this article so anyone that might get interested in mobile app development can reference this if they need a simple working app that requires some kind of a questionnaire to collect data from end users. This article will walk you through on building a simple survey questionnaire feature that can be integrated in your android smart watch app. Generally, a survey is a data gathering method that is utilized to collect, analyze and interpret the views of a group of people.

Before you go any further make sure that you have the necessary requirements for your system and your development environment is properly configured. For setting up the development environment in Visual Studio please refer my previous article here: Getting Started With Android Wearable Using Xamarin and Visual Studio

Let's Get Started!

For this demo, I'm going to use Visual Studio 2015 with Xamarin version 4.0. Open Visual Studio 2015 and then create a new project by selecting File > New > Project. It should bring up the following dialog below:

new

Under Templates select Visual C# > Android > Wear App (Android). Name your app to whatever you like but for the simplicity of this demo I just named it as "Xamarin.Messaging". Click OK to let Visual Studio generate the necessary files for our application. You should now be able to see the following screen:

xamarin

The first thing we need is to add the needed images for our app. In this example, I've added two images under Resources >Drawable folder as shown below:

Drawable

The images we’ve added will be used as the background for the Buttons and the other is for the question indicator.

Setting up the Model.

Now add a new class by right-clicking on the solution project and then select Add > Class. Name the class as "Messaging" and add the following properties below:

  1. using System;  
  2.   
  3. namespace Xamarin.Messaging  
  4. {  
  5.     public class Message  
  6.     {  
  7.         public int MessageID { getset; }  
  8.         public int MemberID { getset; }  
  9.         public short MessageTypeID { getset; }  
  10.         public short IconID { getset; }  
  11.         public string MessageText { getset; }  
  12.         public bool IsQuestion { getset; }  
  13.         public string SelectedAnswer { getset; }  
  14.     }  
  15. }  
The class above represents our view model that mimics the database table schema. But keep in mind that I will not be using any database here for simplicity.

The next step is to add a new class file called "Messaging". This file will contain the logic for our survey feature. Here's the code block below:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using Android.OS;  
  5. using Java.Util;  
  6.   
  7. namespace Xamarin.Messaging  
  8. {  
  9.     #region MESSAGING LOGIC  
  10.     public class Messaging  
  11.     {  
  12.   
  13.         public bool isMessageAvailable { getset; }  
  14.   
  15.         private Queue<Message> Messages { getset; }  
  16.         private List<Message> AnsweredMessages { getset; }  
  17.         private List<Message> RawMessages { getset; }  
  18.   
  19.         public Messaging()  
  20.         {  
  21.             this.Messages = new Queue<Message>();  
  22.             this.AnsweredMessages = new List<Message>();  
  23.         }  
  24.         public enum MessageIconType { None = 0, Question }  
  25.         public enum MessageType { None = 0, Question, Information }  
  26.         public void Add(Message message) { this.Messages.Enqueue(message); }  
  27.   
  28.         public void Update(Message message)  
  29.         {  
  30.             this.Messages.Dequeue();  
  31.             this.AnsweredMessages.Add(message);  
  32.         }  
  33.   
  34.         public Queue<Message> GetMessages()  
  35.         {  
  36.             var messages = GetMessagesByMemberID(1);  
  37.             messages.ToList().ForEach(x => this.Add(x));  
  38.             return this.Messages;  
  39.         }  
  40.   
  41.         private List<Message> GetMessagesByMemberID(int memberID)  
  42.         {  
  43.             this.RawMessages = new List<Message>();  
  44.             this.RawMessages.Add(new Message() { MessageID = 1, MemberID = 1, MessageTypeID = 2, IconID = 1, MessageText = "Do you drink beer?", IsQuestion = true, SelectedAnswer = "" });  
  45.             this.RawMessages.Add(new Message() { MessageID = 2, MemberID = 1, MessageTypeID = 2, IconID = 2, MessageText = "Do you like to code?", IsQuestion = true, SelectedAnswer = "" });  
  46.             this.RawMessages.Add(new Message() { MessageID = 3, MemberID = 1, MessageTypeID = 3, IconID = 0, MessageText = "Thank you.", IsQuestion = false, SelectedAnswer = "" });  
  47.             return this.RawMessages;  
  48.         }  
  49.   
  50.     }  
  51.     #endregion  
  52.  
  53.     #region RUNNABLE  
  54.     public class MessageTimerTask : TimerTask  
  55.     {  
  56.         MessagingRunnableTask MRT;  
  57.         public MessageTimerTask(MessagingRunnableTask mrt) { MRT = mrt; }  
  58.         public override void Run()  
  59.         {  
  60.             MRT._handler.Post(new Action(() =>  
  61.             {  
  62.                 RedirectToView(MRT);  
  63.             }));  
  64.         }  
  65.   
  66.         void RedirectToView(MessagingRunnableTask mrt)  
  67.         {  
  68.             MainActivity m = new MainActivity();  
  69.             if (mrt._isSurveryDone)  
  70.                 m.SetView("Home");  
  71.             else  
  72.                 m.SetView("Survey");  
  73.         }  
  74.     }  
  75.     public class MessagingRunnableTask  
  76.     {  
  77.         MessageTimerTask _showMessageTask;  
  78.         Timer _showMessageTimer;  
  79.         public Handler _handler;  
  80.         public bool _isSurveryDone = false;  
  81.   
  82.         public MessagingRunnableTask() { _handler = new Handler(); }  
  83.         public void ProcessMessageTask(int waitInMilliseconds, bool isDone)  
  84.         {  
  85.             _isSurveryDone = isDone;  
  86.             _showMessageTask = new MessageTimerTask(this);  
  87.             _showMessageTimer = new Timer();  
  88.             _showMessageTimer.Schedule(_showMessageTask, waitInMilliseconds);  
  89.         }  
  90.     }  
  91.     #endregion  
  92. }  
The "Messaging" class houses some properties and methods that will be used in the application. The main idea here is to use Queue<T> for storing the list of messages from the data source so we can easily pop-out the message based on the sequence of data from the queue. The Add() method basically adds new message to the queue. The Update() method removes an existing message from the Message queue object and add a new message to the AnsweredMessages list object. GetMessages() method gets all messages that are available for a particular user. GetMessagesByMemberID() is a method where we set some dummy data to test our survey feature. In real scenarios, you may need to get the data from your database.

You may also have noticed that the file also contains the implementation for runnable. This is to schedule the survey questionnaire within a given period of time. This means that you can control as to when to prompt the users with the survey questions.

Adding the Layout

Now add a new .AXML file under Resources > Layout. Name the layout as "MessageView" as shown in the figure below:

layout

After that switch to "Source" view in the designer and then add the following markup below:
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.                 android:orientation="vertical"  
  4.                 android:layout_width="fill_parent"  
  5.                 android:layout_height="fill_parent"  
  6.                 android:layout_centerHorizontal="true"  
  7.                 android:maxLines="10"  
  8.                 android:scrollbars="vertical">  
  9.     <ImageView android:layout_centerHorizontal="true"  
  10.                android:layout_marginTop="15.0dp"  
  11.                android:layout_width="55px"  
  12.                android:layout_height="50px"  
  13.                android:id="@+id/imgHolder" />  
  14.     <ScrollView android:minWidth="25px"  
  15.                 android:minHeight="25px"  
  16.                 android:layout_width="match_parent"  
  17.                 android:layout_height="wrap_content"  
  18.                 android:layout_centerHorizontal="true"  
  19.                 android:layout_below="@+id/imgHolder"  
  20.                 android:id="@+id/scrollView1">  
  21.         <TableLayout android:layout_width="fill_parent"  
  22.                      android:layout_height="fill_parent"  
  23.                      android:stretchColumns="1">  
  24.             <TextView android:text=""  
  25.                       android:id="@+id/txtQuestion"  
  26.                       android:layout_centerHorizontal="true"  
  27.                       android:layout_width="wrap_content"  
  28.                       android:layout_height="wrap_content"  
  29.                       android:textSize="40dp"  
  30.                       android:layout_marginBottom="10.0dp"  
  31.                       android:gravity="center"  
  32.                       android:layout_below="@+id/imgHolder" />  
  33.             <LinearLayout android:orientation="horizontal"  
  34.                           android:paddingLeft="4.0dip"  
  35.                           android:paddingTop="5.0dip"  
  36.                           android:paddingRight="4.0dip"  
  37.                           android:paddingBottom="1.0dip"  
  38.                           android:layout_width="fill_parent"  
  39.                           android:layout_height="wrap_content"  
  40.                           android:layout_below="@+id/txtQuestion"  
  41.                           android:layout_marginBottom="10.0dp"  
  42.                           android:gravity="center"  
  43.                           android:id="@+id/linearLayoutQuestionButtons">  
  44.                 <Button android:id="@+id/btnYes"  
  45.                         android:layout_width="114px"  
  46.                         android:layout_height="46px"  
  47.                         android:text="YES"  
  48.                         android:background="@drawable/imgMessagingButton" />  
  49.                 <Button android:id="@+id/btnNo"  
  50.                         android:layout_width="114px"  
  51.                         android:layout_height="46px"  
  52.                         android:text="NO"  
  53.                         android:background="@drawable/imgMessagingButton" />  
  54.                 <Button android:id="@+id/btnOk"  
  55.                         android:layout_width="114px"  
  56.                         android:layout_height="46px"  
  57.                         android:text="OK"  
  58.                         android:layout_centerHorizontal="true"  
  59.                         android:background="@drawable/imgMessagingButton"  
  60.                         android:visibility="gone" />  
  61.             </LinearLayout>  
  62.         </TableLayout>  
  63.     </ScrollView>  
  64. </RelativeLayout>  
The markup above simply displays some Buttons, TextBox and an Image for the survey. The next step is to modify the "RectangleMain.axml" file to make it look like below:
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.               xmlns:tools="http://schemas.android.com/tools"  
  4.               android:orientation="vertical"  
  5.               android:layout_width="fill_parent"  
  6.               android:layout_height="fill_parent"  
  7.               tools:context=".MainActivity"  
  8.               tools:deviceIds="wear_round">  
  9.     <TextView android:text="Xamarin.Android Messaging Survey Sample"  
  10.               android:id="@+id/txtWelcome"  
  11.               android:layout_centerHorizontal="true"  
  12.               android:layout_width="wrap_content"  
  13.               android:layout_height="wrap_content"  
  14.               android:textSize="40dp"  
  15.               android:layout_marginBottom="10.0dp"  
  16.               android:gravity="center" />  
  17.     <LinearLayout android:orientation="vertical"  
  18.                   android:id="@+id/layoutMessaging"  
  19.                   android:layout_width="fill_parent"  
  20.                   android:layout_height="fill_parent">  
  21.         <include layout="@layout/messageview" />  
  22.     </LinearLayout>  
  23. </LinearLayout>  
The markup above includes the "MessageView" layout that we have just created earlier to the main layout.

Adding the Functionality

Now create a new class file and name it as "MessageScreen". This file serves as the code behind for the survey logic. Here's the code block below:
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using Android.Views;  
  5. using Android.Widget;  
  6.   
  7. namespace Xamarin.Messaging  
  8. {  
  9.     public partial class MainActivity  
  10.     {  
  11.         MessagingRunnableTask _MRT;  
  12.         bool _isQuestionAvailable = true;  
  13.         bool _isMessageAQuestion = true;  
  14.         Messaging _message;  
  15.         Queue<Message> _availableMessages;  
  16.         TextView _txtQuestion;  
  17.         Button _btnYes;  
  18.         Button _btnNo;  
  19.         Button _btnOk;  
  20.         LinearLayout _llQuestionButtons;  
  21.         ImageView _imgMessageIcon;  
  22.         Messaging.MessageIconType _messageIconType;  
  23.         Messaging.MessageType _messageType;  
  24.   
  25.         public void DisplayMessage()  
  26.         {  
  27.             InitializeObjects();  
  28.             _availableMessages = _message.GetMessages();  
  29.             _llQuestionButtons.Visibility = ViewStates.Visible;  
  30.             _isQuestionAvailable = true;  
  31.   
  32.             ShowQuestion();  
  33.   
  34.             RunOnUiThread(WireQuestionButtons);  
  35.   
  36.         }  
  37.   
  38.         void AnswerNo_Click(object sender, EventArgs e) { ShowNextQuestion(); }  
  39.         void AnswerYes_Click(object sender, EventArgs e) { ShowNextQuestion(); }  
  40.         void AnswerOk_Click(object sender, EventArgs e) { ShowNextQuestion(); }  
  41.   
  42.         void WireQuestionButtons()  
  43.         {  
  44.             _btnNo.Click -= AnswerNo_Click;  
  45.             _btnNo.Click += AnswerNo_Click;  
  46.             _btnYes.Click -= AnswerYes_Click;  
  47.             _btnYes.Click += AnswerYes_Click;  
  48.   
  49.             _btnOk.Click -= AnswerOk_Click;  
  50.             _btnOk.Click += AnswerOk_Click;  
  51.         }  
  52.         void InitializeObjects()  
  53.         {  
  54.             _btnNo = FindViewById<Button>(Resource.Id.btnNo);  
  55.             _btnYes = FindViewById<Button>(Resource.Id.btnYes);  
  56.             _btnOk = FindViewById<Button>(Resource.Id.btnOk);  
  57.             _txtQuestion = FindViewById<TextView>(Resource.Id.txtQuestion);  
  58.             _llQuestionButtons = FindViewById<LinearLayout>(Resource.Id.linearLayoutQuestionButtons);  
  59.             _imgMessageIcon = FindViewById<ImageView>(Resource.Id.imgHolder);  
  60.   
  61.             _MRT = new MessagingRunnableTask();  
  62.             _message = new Messaging();  
  63.         }  
  64.   
  65.         void ToogleButtons()  
  66.         {  
  67.             if (_isMessageAQuestion)  
  68.             {  
  69.                 _btnNo.Visibility = ViewStates.Visible;  
  70.                 _btnYes.Visibility = ViewStates.Visible;  
  71.                 _btnOk.Visibility = ViewStates.Gone;  
  72.             }  
  73.             else  
  74.             {  
  75.                 _btnNo.Visibility = ViewStates.Gone;  
  76.                 _btnYes.Visibility = ViewStates.Gone;  
  77.                 _btnOk.Visibility = ViewStates.Visible;  
  78.             }  
  79.         }  
  80.         void ToggleIcon(Messaging.MessageIconType iconType)  
  81.         {  
  82.             switch (iconType)  
  83.             {  
  84.                 case Messaging.MessageIconType.None:  
  85.                     {  
  86.                         _imgMessageIcon.SetImageResource(0);  
  87.                         break;  
  88.                     }  
  89.                 case Messaging.MessageIconType.Question:  
  90.                     {  
  91.                         _imgMessageIcon.SetImageResource(Resource.Drawable.imgMessagingQuestion);  
  92.                         break;  
  93.                     }  
  94.             }  
  95.         }  
  96.         void ToggleButtonText(Messaging.MessageType messageType)  
  97.         {  
  98.             switch (messageType)  
  99.             {  
  100.                 case Messaging.MessageType.Information:  
  101.                     {  
  102.                         _btnOk.Text = "OK";  
  103.                         break;  
  104.                     }  
  105.             }  
  106.         }  
  107.   
  108.         void ShowQuestion()  
  109.         {  
  110.             if (_availableMessages.Any())  
  111.             {  
  112.                 var m = _availableMessages.First();  
  113.                 _txtQuestion.Text = m.MessageText;  
  114.                 _isMessageAQuestion = m.IsQuestion;  
  115.                 _messageIconType = (Messaging.MessageIconType)m.IconID;  
  116.                 _messageType = (Messaging.MessageType)m.MessageTypeID;  
  117.   
  118.                 _message.Update(m);//deque  
  119.   
  120.                 if (!_availableMessages.Any())  
  121.                     _isQuestionAvailable = false;  
  122.   
  123.                 ToggleIcon(_messageIconType);  
  124.                 ToogleButtons();  
  125.                 ToggleButtonText(_messageType);  
  126.             }  
  127.             else  
  128.                 _isQuestionAvailable = false;  
  129.         }  
  130.         void ShowNextQuestion()  
  131.         {  
  132.             if (_isQuestionAvailable)  
  133.                 ShowQuestion();  
  134.             else  
  135.             {  
  136.                 _btnNo.Visibility = ViewStates.Gone;  
  137.                 _btnYes.Visibility = ViewStates.Gone;  
  138.                 _llQuestionButtons.Visibility = ViewStates.Gone;  
  139.                 _txtQuestion.Text = "Saving...";  
  140.                 SaveAndBackToPreviousView(1000);  
  141.             }  
  142.         }  
  143.         void SaveAndBackToPreviousView(int waitInMilliseconds)  
  144.         {  
  145.             _MRT.ProcessMessageTask(waitInMilliseconds, true);  
  146.         }  
  147.     }  
  148. }  
The class above is a partial class that implements the logic and functionality for our survey feature. It is where we initialized the controls and wired some event handlers for Buttons. It also contains some methods to do certain actions based on user interaction. For example the ToggleButtons() will show or hide a specific Button based on the question presented to the user. The rest of the methods are very much self-explanatory as the method name suggest.

Next is open up "MainActivity" class and update the code with the follwing:
  1. using System;  
  2.   
  3. using Android.App;  
  4. using Android.Content;  
  5. using Android.Views;  
  6. using Android.Widget;  
  7. using Android.OS;  
  8.   
  9. namespace Xamarin.Messaging  
  10. {  
  11.     [Activity(Label = "Xamarin.Messaging", MainLauncher = true, Icon = "@drawable/icon")]  
  12.     public partial class MainActivity : Activity  
  13.     {  
  14.         protected override void OnCreate(Bundle bundle)  
  15.         {  
  16.             base.OnCreate(bundle);  
  17.   
  18.             // Set our view from the "main" layout resource  
  19.             SetContentView(Resource.Layout.RectangleMain);  
  20.   
  21.             RegisterSwitchViewtReciever();  
  22.   
  23.             MessagingRunnableTask task = new MessagingRunnableTask();  
  24.             task.ProcessMessageTask(5000, false);  
  25.         }  
  26.   
  27.         public void SetView(string viewName)  
  28.         {  
  29.             Intent intent = new Intent("SwitchView");  
  30.             intent.PutExtra("view", viewName);  
  31.             Application.Context.SendBroadcast(intent);  
  32.         }  
  33.         public void Home()  
  34.         {  
  35.             FindViewById<TextView>(Resource.Id.txtWelcome).Visibility = ViewStates.Visible;  
  36.             FindViewById<LinearLayout>(Resource.Id.layoutMessaging).Visibility = ViewStates.Gone;  
  37.         }  
  38.   
  39.         SwitchViewReciever switchViewReciever;  
  40.         void RegisterSwitchViewtReciever()  
  41.         {  
  42.             switchViewReciever = new SwitchViewReciever();  
  43.             switchViewReciever.ActionOnRecieve = SwitchView;  
  44.             IntentFilter filter = new IntentFilter("SwitchView");  
  45.             RegisterReceiver(switchViewReciever, filter);  
  46.         }  
  47.   
  48.         public void SwitchView(string view)  
  49.         {  
  50.   
  51.             FindViewById<TextView>(Resource.Id.txtWelcome).Visibility = view == "Home" ? ViewStates.Visible : ViewStates.Gone;  
  52.             FindViewById<LinearLayout>(Resource.Id.layoutMessaging).Visibility = view == "Survey" ? ViewStates.Visible : ViewStates.Gone;  
  53.   
  54.             Action layoutAction;  
  55.             switch (view)  
  56.             {  
  57.                 case "Survey":  
  58.                     layoutAction = DisplayMessage;  
  59.                     break;  
  60.                 default:  
  61.                     layoutAction = Home;  
  62.                     break;  
  63.             }  
  64.             layoutAction.Invoke();  
  65.         }  
  66.   
  67.     }  
  68.   
  69.     public class SwitchViewReciever : BroadcastReceiver  
  70.     {  
  71.         public Action<String> ActionOnRecieve;  
  72.         public override void OnReceive(Context context, Intent intent)  
  73.         {  
  74.             ActionOnRecieve.Invoke(intent.GetStringExtra("view") ?? "Home");  
  75.         }  
  76.     }  
  77. }  
The code above is pretty much self-explanatory too. But the important thing to highlight there is the initialization and calling of the ProcessMessageTask() method. This method is responsible for displaying the survey page after 5 seconds. The RegisterSwitchViewtReciever registers the broadcast receiver so we can have access to the data from the broadcast. The SwitchViewReciever class is used to handle the data from a broadcast by implementing a BroadcastReciever. The broadcast will pass the data and tell the app whether to display the "Home" or "Survey" page.

The Output

Running the app will display like this in a real smart watch device.

On initial load

initial

Displays the question after 5 seconds,

output

After all questions have been shown it will then show a “Thank You” message and return to the home view.

Thanks for reading and I hope someone finds this article useful.

Read more articles on Wearables: