Finding Love on CallRoulette: An Android Studio Adventure

November 05, 2015
Written by

droidlove

After connecting to a guy with an Al Gore mask, a group of loud college kids, and a yodelling expert, I met my current girlfriend of 3 years on ChatRoulette. Despite never having any luck with dating apps like Tinder or OkCupid, I was somehow able to find love there. I think it was precisely because ChatRoulette didn’t allow you to craft a persona beforehand that made it so awesome. It brought the chance encounters of life to the Internet through a series of blind dates that you could opt out of with the click of a button. I think it could be a great experience to have on a mobile device; with Twilio, we can make it happen.

Let’s build an app similar to ChatRoulette for Android devices. We’ll call it CallRoulette and keep things relatively simple to focus on the essentials. No login or signup will be necessary since everyone will be anonymous. The app will have one Activity and a single button that can both start and end a call.  The purpose of the app is to allow the user to have a series of phone calls with random people in the hope that they might be able to find someone interesting. They can easily hang up at anytime by tapping the same button to start over. Since we’re keeping the interface simple, we’ll be able to focus on what’s happening under the hood.

Prerequisites

Configuring the Project

Let’s start by firing up Android Studio and selecting ‘start a new Android Project’.

namingProject

Select ‘Phone and Tablet’ for the next screen and set the minimum SDK to API 15. That way our app will work with the vast majority of Android devices.

APIVersion

Next we select the type of activity we want to have. We can select ‘Empty Activity’ since we don’t really need the templates for this app.

EmptyActivity

Finally, we can customize the activity we want by setting its name and selecting ‘Generate Layout File’. For our example case, we’ll leave the activity name and layout name as default.

customizeActivity

Now we’ll want to add in our Twilio Client library. If you look in the Twilio client library root directory that you downloaded, you’ll see a ‘libs’ folder. In the libs folder, you’ll find a file called twilioclient-android.jar. That’s the Twilio library we’ll be using. Copy that library and paste it into the libs folder of our example app. Then go to your build.gradle and add the following line to the dependencies block.

compile files('libs/twilioclient-android.jar')

So now it will look like this

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:21.0.3'
  compile files('libs/twilioclient-android.jar')
}

We’ll also want to add a set of permissions to our AndroidManifest.xml file, found under the manifests folder

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

These permissions will allow our Android app to send the user’s voice to the Twilio cloud. Once you have the permissions set up in the manifest, your app will be configured and linked properly. Woot! Now we’re ready to start writing the bulk of the logic.

 

Building the UI

Generally, when I’m building an Android app, I’ll create one activity at a time. For each Activity, I follow the same development pattern that goes something like this

 

  1. Create skeleton UI using Layout Editor
  2. Wire any elements necessary to the backend
  3. Add logic to backend elements
  4. Style the UI elements

 

Let’s start by creating the skeleton UI using Android Studio’s Layout Editor. In the case of our app, we’ll want a vertical stack of elements. From top to bottom we’ll have: Logo, title, callTimer, and CallButton. So we’ll want to remove the default relativeLayout and replace it with a vertical layout. Go to your res/main/activity_main.xml and select the text tab instead of the design tab. Replace this

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
</RelativeLayout>

with this

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center_horizontal"
  android:orientation="vertical"
  android:weightSum="1">

The vertical LinearLayout helps align our UI elements with the correct spacing between each other vertically. Once we have that setup, we can drag and drop our elements onto the screen. Our 4 UI elements will each serve a distinct purpose

  1. imageView for our logo. (Drag this png of the Twilio logo to the res/drawable folder of your project. Link it by adding ‘@drawable/twiliologo’ to the src property of the imageView.)
  2. textView for our title.
  3. chronometer for our call timer
  4. callButton for calling

Once you’ve set up the elements, it should look something like this

InterfaceSkeleton

There are certain things you can do to make it easier for your layout to adapt to different screen sizes. If I’m not hardcoding measurements, I generally like to use either ‘wrap_content’ or ‘match_parent’ for my elements’ layout_width and layout_height. ‘wrap_content’ basically says set the element length/width to the minimum size necessary to fit the content within that view, while match_parent says make the element expand to match the size of the parent view.

Android uses DP (Density-independent pixels) which helps express layout dimensions or position without depending on pixel density. You can also set your margin lengths under ‘layout:margin’ in the properties list on the right-hand sidebar of the layout editor. You don’t want an element at an edge of the screen to not appear in a smaller screen size, so margins can help here. Whenever you set a margin between the element and that edge, it will never exceed it.

Wiring our UI elements to our backend

Once we’ve set up all of our elements we can wire them to our backend. Make sure you’ve given each of them a name under the ‘id’ property of the right-hand sidebar.

id

First, let’s go to our mainActivity file (in my case, it’s in my package named twilio.com.twilioclient) and import our dependencies. It will be in your package

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.util.Log;
import android.widget.TextView;
import android.widget.Chronometer;
import android.os.SystemClock;

We’re going to need an event listener for our call button so we can act on it. Let’s implement that in the class declaration so it looks like this

public class MainActivity extends Activity implements View.OnClickListener {

Next, we’re going to need to reference our elements. We’ll make them private variables declared at the beginning of the class and reference them in several methods later on.

private Button callsomeoneButton;
private Chronometer callTimer;

In our onCreate method, we can link our UI elements to the variables we’ve just declared

callsomeoneButton = (Button)  findViewById(R.id.callsomeoneButton);
callsomeoneButton.setOnClickListener(this);
callTimer = (Chronometer) findViewById(R.id.chronometer);

Now we just need to call our onClick method to have our code do something based on whether or not the user has tapped the call button

public void onClick(View view)
{
  if (view.getId() == R.id.callsomeoneButton && callsomeoneButton.getText().toString().equals("Call Someone")) {
      callsomeoneButton.setText("End Call");
      callTimer.setVisibility(view.VISIBLE);
      callTimer.setBase(SystemClock.elapsedRealtime());
      callTimer.start();
  } else if(view.getId() == R.id.callsomeoneButton && callsomeoneButton.getText().toString().equals("End Call")) {
      callsomeoneButton.setText("Call Someone");
      callTimer.setVisibility(view.INVISIBLE);
      callTimer.stop();
      callTimer.setBase(SystemClock.elapsedRealtime());
  }
}

This code essentially says that if the user clicks a button and the text of the button says ‘Call Someone’, start the timer and set the button text to ‘End Call’. If the button says ‘End Call’, which means the call is in progress, restart the timer, make it invisible, and reset the text of the button.

Once this code is in place, we can compile and run the app on an Android device. When the call button is tapped, a timer appear and start counting seconds. The button text should also change. When tapped again, the button text will change and the timer will disappear.

MainActivity.java

package twilio.com.twilioclient;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Chronometer;
import android.os.SystemClock;
import android.app.ProgressDialog;
public class MainActivity extends Activity implements View.OnClickListener {
  //These are our UI elements
  private CallRoulette phone;
  private Button callsomeoneButton;
  private Chronometer callTimer;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      //Here we start our listeners and initialize our elements
      phone = new CallRoulette(this, this);
      callsomeoneButton = (Button)  findViewById(R.id.callsomeoneButton);
      callsomeoneButton.setOnClickListener(this);
      callTimer = (Chronometer) findViewById(R.id.chronometer);
  }
  @Override
  public void onClick(View view)
  {
      //whenever a user taps the call button, connect/disconnect a phone line
      if (view.getId() == R.id.callsomeoneButton && callsomeoneButton.getText().toString().equals("Call Someone")) {
          phone.connect();
          callsomeoneButton.setBackgroundResource(R.drawable.endcallgradient);
          callsomeoneButton.setText("End Call");
          callTimer.setVisibility(view.VISIBLE);
          callTimer.setBase(SystemClock.elapsedRealtime());
          callTimer.start();
      } else if(view.getId() == R.id.callsomeoneButton && callsomeoneButton.getText().toString().equals("End Call")) {
          phone.disconnect();
          callsomeoneButton.setText("Call Someone");
          callsomeoneButton.setBackgroundResource(R.drawable.callgradient);
          callTimer.setVisibility(view.INVISIBLE);
          callTimer.stop();
          callTimer.setBase(SystemClock.elapsedRealtime());
      }
  }
}

 

Adding Logic to Backend Elements

We’ve arrived at the best part of this process. It’s time to integrate the app with Twilio! First things first, we’re going to need 2 new Java classes to make HTTP requests, AsyncHttpTask.java and HttpHandler.java. These 2 classes are totally boilerplate and can be used in almost any Android app that makes network calls. You can find them here. You can drag and drop them into your project under your java folder with MainActivity.

To start off, we’re going to create a class called ‘CallRoulette to handle our Twilio logic. Before we create the class, we can go ahead and reference it in our MainActivity class.

At the list of private variables at the top of the MainActivity class, we can add in a new one

private CallRoulette phone;

and in our onCreate method, we can initialize it

phone = new CallRoulette(this);

Lastly, in our onClick method, if the button string equals “Call Someone” we can add this to the rest of the commands

phone.connect("");

else we’ll add this

phone.disconnect("");

Now that the references are in our MainActivity class, we can create our CallRoulette class. Go to ‘File > new > Java Class’, name it CallRoulette, and click create.

The class will show up right next to the MainActivity class in the project lefthand sidebar. Once we’re in the class, we can go ahead and implement the necessary dependencies

import android.content.Context;
import android.util.Log;
import com.twilio.client.Connection;
import com.twilio.client.Device;
import com.twilio.client.DeviceListener;
import com.twilio.client.Twilio;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import java.util.HashMap;
import java.util.Map;

Make sure the class implements the Twilio listener

public class CallRoulette implements Twilio.InitListener {
}

Before we add our methods, We’ll add the private variables we’ll need to the top of the class

private Device mDevice;
private String TAG = "CallRoulette";
private Connection mConnection;
private Context mContext;

The device and connection are Twilio objects; the device represents your phone and the connection maintains your link to the Twilio server. We’ll use the tag string to identify our log messages. The context gives the Twilio SDK access to application-specific resources and classes.

We can initialize the context in the constructor.

public CallRoulette(Context context)
{
  this.mContext = context;
  Twilio.initialize(context, this);
}

Next, we’ll want to implement our connect and disconnect methods. In our case, the phone number will be an empty parameter since everyone will be random. We’ll use the connect method on device to create a connection.

public void connect()
{
  Map<String, String> parameters = new HashMap<String, String>();
  mConnection = mDevice.connect(parameters, null);
  if (mConnection == null)
      Log.w(TAG, "Failed to create new connection");
}

For our disconnect method, we can utilize the built-in disconnect method on the Twilio connection object. You’ll find that I also have a nested HttpHandler call in this snippet. This is because we’ll need a handler to perform a GET request on the hangup URL in order to tell the server that the client has ‘hung up’ the phone.

public void disconnect()
{
  if (mConnection != null) {
      mConnection.disconnect();
      mConnection = null;
  }
  new HttpHandler(){
      @Override
      public HttpUriRequest getHttpRequestMethod(){
          Log.d(TAG, mContext.getString(R.string.app_hangup_url));
          return new HttpGet(mContext.getString(R.string.app_hangup_url));
      }
      @Override
      public void onResponse(String token) {
          Log.d(TAG, token);
      }
  }.execute();
}

The disconnect method does two things, it ends the connection, retrieves the capability token, and tells the server to ‘hangup’ via a get request. We’ll only receive the capability token when we’ve authenticated successfully with the server, and it allows us to sign our communications from the device without exposing our AuthToken.

The connect method looks similar to the disconnect method

public void onInitialized(){
  Log.d(TAG, "Twilio SDK is ready");
  new HttpHandler(){
      @Override
      public HttpUriRequest getHttpRequestMethod(){
          Log.d(TAG, mContext.getString(R.string.app_capability_url));
          return new HttpGet(mContext.getString(R.string.app_capability_url));
      }
      @Override
      public void onResponse(String token) {
          mDevice = Twilio.createDevice(token, null);
          Log.d(TAG, "Capability token: " + token);
      }
  }.execute();
}

Lastly, we write some logic for the finalize method. This method is called by the Java garbage collector at runtime to help clear objects from memory. We’ll want to release our device if there are no more references to it.

@Override
protected void finalize()
{
  if (mDevice != null)
      mDevice.release();
}

I’m sure you’ve noticed how I have references to URLs that don’t exist in our class in both HttpHandler methods. I’m referencing the default urls.xml file found under res/values. The only default string name will be the app_name, in our case ‘CallRoulette’. We’re going to add two more

<string name="app_capability_url"></string>
<string name="app_hangup_url"></string>

Now we’ll need to fill in those URLs with a link to our server. We’ll get to the server part last, so let’s finish up our UI with a little styling.

 

Styling our UI

I’ve attached two gradient XML files here that you can use . You can drop them into your res/drawable folder. Once there, we’ll be able to reference them gradients in the background property of the button by filling it with either @drawable/callgradient or @drawable/endcallgradient.

Then back in the MainActivity, we change the background gradient based on whether or not the user is currently in a call.

public void onClick(View view)
{
  if (view.getId() == R.id.callsomeoneButton && callsomeoneButton.getText().toString().equals("Call Someone")) {
      phone.connect();
      callsomeoneButton.setBackgroundResource(R.drawable.endcallgradient);
      callsomeoneButton.setText("End Call");
      callTimer.setVisibility(view.VISIBLE);
      callTimer.setBase(SystemClock.elapsedRealtime());
      callTimer.start();
  } else if(view.getId() == R.id.callsomeoneButton && callsomeoneButton.getText().toString().equals("End Call")) {
      phone.disconnect();
      callsomeoneButton.setText("Call Someone");
      callsomeoneButton.setBackgroundResource(R.drawable.callgradient);
      callTimer.setVisibility(view.INVISIBLE);
      callTimer.stop();
      callTimer.setBase(SystemClock.elapsedRealtime());
  }
}

That’s it for our client-side logic! Now we can setup the server. If you  haven’t done much server-side development, don’t worry. It’s 10x easier than you may be thinking right now.

CallRoulette.java:

package twilio.com.twilioclient;
import android.app.ProgressDialog;
import android.content.Context;
import android.util.Log;
import com.twilio.client.Connection;
import com.twilio.client.Device;
import com.twilio.client.DeviceListener;
import com.twilio.client.Twilio;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import java.util.HashMap;
import java.util.Map;
import android.view.View;
import android.app.Activity;
import android.widget.ProgressBar;
public class CallRoulette implements Twilio.InitListener
{
   //Let's declare our vars here
  private Device mDevice;
  private String TAG = "CallRoulette";
  private Connection mConnection;
  private Context mContext;
   private ProgressBar pBar;
   public Activity activity;
   public CallRoulette(Context context, Activity _activity)
  {
      //here we initialize Twilio Client
      this.mContext = context;
      Twilio.initialize(context, this);
      //and connect the progressbar element
      this.activity = _activity;
      pBar = (ProgressBar)this.activity.findViewById(R.id.progressBar);
  }
  @Override
  public void onInitialized(){
      Log.d(TAG, "Twilio SDK is ready");
      new HttpHandler(){
          @Override
          public HttpUriRequest getHttpRequestMethod(){
              Log.d(TAG, mContext.getString(R.string.app_capability_url));
              return new HttpGet(mContext.getString(R.string.app_capability_url));
          }
          @Override
          public void onResponse(String token) {
              mDevice = Twilio.createDevice(token, null);
              Log.d(TAG, "Capability token: " + token);
              pBar.setVisibility(View.INVISIBLE);
          }
      }.execute();
  }
  /* Twilio.InitListener method */
  @Override
  public void onError(Exception e) {
      Log.e(TAG, "Twilio SDK couldn't start: " + e.getLocalizedMessage());
  }
  public void connect()
  {
      Map<String, String> parameters = new HashMap<String, String>();
      mConnection = mDevice.connect(parameters, null);
      if (mConnection == null)
          Log.w(TAG, "Failed to create new connection");
  }
  public void disconnect()
  {
      if (mConnection != null) {
          mConnection.disconnect();
          mConnection = null;
      }
      new HttpHandler(){
          @Override
          public HttpUriRequest getHttpRequestMethod(){
              Log.d(TAG, mContext.getString(R.string.app_hangup_url));
              return new HttpGet(mContext.getString(R.string.app_hangup_url));
          }
          @Override
          public void onResponse(String token) {
              Log.d(TAG, token);
          }
      }.execute();
  }
  @Override
  protected void finalize()
  {
      if (mDevice != null)
          mDevice.release();
  }
}

Setting up the TwiML Server

Setting up a TwiML app does several things for us. It helps determine what a user is allowed to do with the Twilio API using the Twilio Markup Language (TwiML). These permissions include things like the ability to make outbound phone calls and the ability to join a queue. It also provides a unique string identifier for the user so that the server can identify them in case they need to be reached via a voice call. So whenever a user taps the call button in our Android app, the TwiML server will generate instructions to determine what happens with the call. Here’s a picture for more clarity

twiml

TwiML is a pretty intuitive language and if you’re interested we have some examples online. Check out this link below to learn more about it.

You’ll need to register your TwiML app with Twilio Name it “ChatRoulette” and leave the rest of the fields blank for now. Save it when you’re done, we’ll come back to this.

To get you started quickly, I’ve pre-written the web app in Python. All you’ll have to do is click a single button to deploy it. The source code for the TwiML web app can be found here.

Once we press the ‘Deploy to Heroku’ button and it will generate a heroku link to the web app on our account.  We’ll see 4 empty fields

Caller ID

App Sid (Click on your app and you will see it listed as under properties)

AccountSid & AuthToken

Once we’ve filled in the fields, click ‘deploy for free’ and the app will be deployed. It utilizes the Twilio Queue API to create a series of connections between different people. Everytime someone calls our server, we check if our queue is empty or not. If it’s empty, we put the user into the queue. They will listen to a pre-recorded Mp3 while they wait to connect to someone. If the queue isn’t empty, the user is connected to whoever is in the queue, and the queue returns to being empty. The queue then never reaches a size greater than 1! If a user in a queue hangs up, our client app calls the /hangup endpoint which empties the queue.

Now that we have a working link to your web app, we can paste it into the browser and hit enter. If all goes well, we’ll see a message on the page that says ‘Welcome to Twilio!’. If it does, congrats! The web app is working.

Back in our TwiML app we can paste in our request URL for the voice field. The link will be

https://your-web-app.herokuapp.com/roulette

The last thing we need to do is insert the link into our client library, in our urls.xml file. It will look like this

<string name="app_capability_url">https:/your-web-app.herokuapp.com/token</string>

<string name="app_hangup_url">https://your-web-app.herokuapp.com/hangup</string>

That’s it! We’re done with the programming portion, so let’s test out our app to see if it works!

 

Testing the App

Let’s go to our TwiML web app’s page and click the call button. You should hear an automated voice say “Please hold.” followed by some music. If so, congratulations on getting at least half of the app to work!  Now we need to try another client to see if it connects to the caller in the queue. Keep the call going and fire up the app on your Android device. Tap the call button and wait. You should hear an automated voice say ‘You have been connected”. Speak into your phone, if you hear your voice coming out of your computer speakers, congrats! You’ve successfully created a session. The app can support multiple sessions as needed.

 

What’s Next?

We’ve successfully created an app using Twilio Client and Android Studio!

Check out the finished code on GitHub. The future of the web is on mobile and Android devices are the most ubiquitous mobile devices on the planet. There are still so many untapped opportunities to help people communicate with each other. Twilio Client is a great tool to make it happen.
Soon, with Twilio Video you’ll be able to add real-time video to conversations and with Twilio’s IP Messaging you’ll be able to add even more messaging functionality to your app. Whether you build a dating app or something else entirely, the web is about communicating with other humans, and that’s exactly what Twilio helps you do best. Hopefully I’ve given you some things to think about, and feel free to tweet us @twilio to tell us what you’re building!