Android Speech Recognition Example

Getting user input via keyboard is OK, but enabling user to just speak rather than having to type is 21st century. Today we will look at Google Speech Recognition and try to integrate it in our android application. It asks user to speak what they intend to enter. It then takes that audio input from user to server, processes it, and returns us output in String format.

CREATE A LAYOUT

Lets first create a button in our main layout.

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Speak up!"
android:id="@+id/button"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />

And a TextView to display the result.


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text=" "
android:id="@+id/textView"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />

CHECK IF INTERNET CONNECTED

On this button click, we will first check if Internet is connected. If connected, we move ahead to bring our speech recognizer up.

 speakUpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if (isInternetConnected()) {
                 // bring our speech recognizer
                } else {
                    Toast.makeText(getApplicationContext(), "Internet is not connected!", Toast.LENGTH_SHORT).show();
                }
            }
        });

We check if Internet is connected or not with following snippet of code:

public boolean isInternetConnected() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo net = cm.getActiveNetworkInfo();
        if (net != null && net.isAvailable() && net.isConnected()) {
            return true;
        } else {
            return false;
        }
    }

SUMMON GOOGLE SPEECH RECOGNIZER

If we are connected to Internet, let the show begin!

if (isInternetConnected()) {
                    Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
                    intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
                    startActivityForResult(intent, REQUEST_CODE);
                } else {
                    Toast.makeText(getApplicationContext(), "Internet is not connected!", Toast.LENGTH_SHORT).show();
                }

We are starting speech recognition activity but we need to get output from that activity into our activity so we have used startActivityForResult(). The REQUEST_CODE is a variable which holds an integer defined in starting. When speech recognition is done doing what it does best, we can retrieve result in our onActivityResult() method.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
            result_tv.setText(data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).get(0).toString());
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

Notice that we are displaying first result returned in data Intent. Actually, it returns suggestions according to what user speaks. We can display these results in a dialog and ask user to select most appropriate result. We are not getting into it. Check out full source code here on Github.

See it in action:

Android Speech Recognition example in action

Sending crash reports with ACRA over email (using Mandrill)

Sending crash reports with ACRA over email (using Mandrill)

 What is ACRA?

ACRA stands for Application Crash Reports for Android. It is library enabling Android applications to send crash reports to developer in order to debug and get insights if/when application crashes or behaves erroneously. As stated on their github page, ACRA is running on over a billion devices.

What makes ACRA an essential part of every Android application is that it can send crash report without user interaction, even when application is not yet launched on Google Play. It comes handy when application is under internal testing or UAT or if its an enterprise private app.

ACRA provides silent reports, toasts, status bar notification with dialog and direct dialog. We are concerned with silent reports where user won’t be notified when an app crashes and a crash report is sent to developer in background.

In this post we are looking at how to send this crash report over email, we are going to accomplish it using MandrillApp. MandrillApp is an email infrastructure service. It doesn’t get simpler than this. Keep reading.

CREATE AN ACCOUNT ON MANDRILLAPP

 Sign up for it here . Same account can then be used for multiple applications. After logging in, you will see a dashboard like below.

 

Mandrill Dashboard

Go to “Settings” in left pane, select “SMTP and API info”.

 Mandrill settings screenshot

After selecting SMTP and API info, click on “New API key”.

Create a new API key for your application, enter a description for you app. You have to create multiple keys for different applications.

 Note: If you added ACRA in an application long time ago and now that applications is no longer supported or you don’t wish to receive crash reports any longer then you can edit/delete the key from this section. 

You can see your recently key created. Copy and save this key, we will use this key in our application to send crash reports over email.

 CREATE A NEW SAMPLE PROJECT

 Let’s create a dummy project to illustrate what we are doing. I have only added a line to make the app crash just upon launching.

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int x = 1 / 0;

    }


}

CONFIGURE PROJECT TO USE ACRA

Now we need to include ACRA in our project. If you are using AndroidStudio/Gradle then you can include following in build.gradle and be done with it.

compile 'ch.acra:acra:4.5.0'

Alternatively, you can add acra-4.X.Y.jar jar as well. 

Now, you need to add an Application class to your project. If you already have an Application class then don’t add, just add ACRA to it.

Create a class and name it like “MyApp” or something. This class should extend Application.

 We will add @ReportsCrashes annotation just above the class name with our mandrill app request URL as formUri (as advised on https://github.com/ACRA/acra/wiki/BasicSetup)

 We also need to override onCreate() method of our newly created class and add following: 


ACRA.init(this);

This is how our class looks like now:

@ReportsCrashes(formKey = "", // will not be used
        formUri = "http://mandrillapp.com/api/1.0/messages/send.json"
)

public class MyApp extends Application {
    private ReportsCrashes mReportsCrashes;

    @Override
    public void onCreate() {
        super.onCreate();

        ACRA.init(this);

    }
}

SETUP ACRA TO SEND EMAIL

So far, ACRA is all set to capture error reports but it will just sit on it. To make it send email to your email address, we need to add JsonSender class. 

Create a class with name JsonSender. Paste following in that class.



public class JsonSender implements ReportSender {
 private Uri mFormUri = null;
 private Map<ReportField, String> mMapping = null;
 private static final String CONTENT_TYPE;

static {
 CONTENT_TYPE = "application/json";
 }

/**
 * <p>
 * Create a new HttpPostSender instance.
 * </p>
 *
 * @param formUri The URL of your server-side crash report collection script.
 * @param mapping If null, POST parameters will be named with
 * {@link org.acra.ReportField} values converted to String with
 * .toString(). If not null, POST parameters will be named with
 * the result of mapping.get(ReportField.SOME_FIELD);
 */
 public JsonSender(String formUri, Map<ReportField, String> mapping) {
 mFormUri = Uri.parse(formUri);
 mMapping = mapping;
 }

public void send(CrashReportData report) throws ReportSenderException {

try {
 URL reportUrl;
 reportUrl = new URL(mFormUri.toString());
 Log.d(LOG_TAG, "Connect to " + reportUrl.toString());

JSONObject json = createJSON(report);

sendHttpPost(json.toString(), reportUrl, ACRA.getConfig().formUriBasicAuthLogin(), ACRA.getConfig().formUriBasicAuthPassword());

} catch (Exception e) {
 throw new ReportSenderException("Error while sending report to Http Post Form.", e);
 }

}

private static boolean isNull(String aString) {
 return aString == null || aString == null;

}

private JSONObject createJSON(Map<ReportField, String> report) {
 JSONObject json = new JSONObject();

ReportField[] fields = ACRA.getConfig().customReportContent();
 if (fields.length == 0) {
 fields = ACRAConstants.DEFAULT_REPORT_FIELDS;
 }
 for (ReportField field : fields) {
 try {
 if (mMapping == null || mMapping.get(field) == null) {
 json.put(field.toString(), report.get(field));
 } else {
 json.put(mMapping.get(field), report.get(field));
 }
 } catch (JSONException e) {
 Log.e("JSONException", "There was an error creating JSON", e);
 }
 }

return json;
 }

//TODO: login + password
 //(isNull(login) ? null : login, isNull(password) ? null : password);
 private void sendHttpPost(String data, URL url, String login, String password) {
 DefaultHttpClient httpClient = new DefaultHttpClient();
 try {
 HttpPost httPost = new HttpPost(url.toString());

List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();

JSONObject jsonObject = new JSONObject();

data = data.replace("\\n", " \n ");
 data = data.replace("\\", "");
 StringEntity se = new StringEntity(getDataForMandrill(data));
 httPost.setEntity(se);

//sets a request header so the page receving the request
 //will know what to do with it
 httPost.setHeader("Accept", "application/json");
 httPost.setHeader("Content-type", "application/json");


 HttpResponse httpResponse = httpClient.execute(httPost);

Log.d(LOG_TAG, "Server Status: " + httpResponse.getStatusLine());
 Log.d(LOG_TAG, "Server Response: " + EntityUtils.toString(httpResponse.getEntity()));


 } catch (ClientProtocolException e) {
 e.printStackTrace();
 } catch (UnsupportedEncodingException e) {
 e.printStackTrace();
 } catch (IOException e) {
 e.printStackTrace();
 } finally {
 httpClient.getConnectionManager().shutdown();
 }
 }

private String getDataForMandrill(String data) {


 try {

JSONObject email_json = new JSONObject();
 email_json.put("email", "ay********al@gmail.com"); // recipient email address goes here
 email_json.put("name", "Ayush"); // receiver's name..can be changed
 email_json.put("type", "to");

JSONArray email_array = new JSONArray();
 email_array.put(email_json);

JSONObject message_json = new JSONObject();
 message_json.put("html", "");
 message_json.put("text", "Logs: \n" + data);
 message_json.put("subject", "My Android Error Report"); // add your own subject
 message_json.put("from_email", "error@android.com"); // change if your preferred "from email"
 message_json.put("from_name", "Automatic Error Report"); // change it to your preferred name
 message_json.put("to", email_array);

JSONObject jsonObject = new JSONObject();
 jsonObject.put("key", "************5kGY2OhyA");
 jsonObject.put("message", message_json);

return jsonObject.toString();

} catch (JSONException e) {
 e.printStackTrace();
 }

return null;
 }


}


Look closely at method getDataForMandrill() where you have to change receiver’s email address, name, sender’s email address, sender’s name, email subject and you are good to go. You also have to add the key here which we acquired above from mandrillapp.

Now that we have this helper class ready, lets make final changes to MyApp class and make it use this recently created class. Paste following in MyApp:


 mReportsCrashes = this.getClass().getAnnotation(ReportsCrashes.class);
 JsonSender jsonSender = new JsonSender(mReportsCrashes.formUri(), null);
 ACRA.getErrorReporter().setReportSender(jsonSender);

So that finally MyApp looks like this:


@ReportsCrashes(formKey = "", // will not be used
 formUri = "http://mandrillapp.com/api/1.0/messages/send.json"
)

public class MyApp extends Application {
 private ReportsCrashes mReportsCrashes;

@Override
 public void onCreate() {
 super.onCreate();

ACRA.init(this);

mReportsCrashes = this.getClass().getAnnotation(ReportsCrashes.class);
 JsonSender jsonSender = new JsonSender(mReportsCrashes.formUri(), null);
 ACRA.getErrorReporter().setReportSender(jsonSender);

}
}


UPDATE MANIFEST

We need Internet persmission to be able to send reports across. Add following to manifest file.

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

And, add “android:name” attribute to application element like following:

 <application
android:allowBackup="true"
android:name=".MyApp"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">

This is pretty much it. Let’s execute it and get email with crash report.

Error email

Full source code for this application is available at github.

Android Custom Toggle Button Example (iOS like toggle buttons)

Toggle buttons are a great way of getting user input when we need only either yes or no from user. Technically speaking, when we need user input in boolean form- toggle buttons can come handy.

Adding a toggle button to your view and getting its status is very easy, you can have a look what Google has to say about them here.

But, toggle buttons provided with sdk are slightly dull and you just can’t go around and use them everywhere. Well, you can use different images for toggle button states. You can define a button for its checked state and other button for unchecked state. With this you can get a switch-look also.

Lets look how you can get a custom toggle button in your application. The images we are using for different states are inspired from toggle button of iOS, I found them classy (lets have a bit open mind here 😉 ).

In action:

Custom toggle button android

Full source code at github.

 Put toggle_off.png and toggle_on.png in drawable folder. You can replace them with your own images.

toggle_on
toggle_off

Create toggle_selector.xml in drawable. Define drawables for different states.

toggle_selector.xml

<?xml version="1.0"encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item
android:drawable="@drawable/toggle_on"
android:state_checked="true"/>

   <item
android:drawable="@drawable/toggle_off"
android:state_checked="false"/>
</selector>

Define ToggleButton in activity_main.xml


<ToggleButton
        android:id="@+id/toggleButton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="@drawable/toggle_selector"
        android:checked="false"
        android:text=""
        android:textOff=""
        android:textOn="" />

Important: We have to set background of our toggle button to toggle_selector defined in drawable folder. It will set the image according to its state. Also, to get desired effect as shown in demo above – we must set android:textOff and android:textOn to empty string otherwise text will appear on top of our images.

To get the status from ToggleButton, we can set a onCheckedChangeListener on our ToggleButton as shown below.

toggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton arg0, boolean isChecked) {
				text.setText("Status: " + isChecked);
			}
		});

We are updating text of TextView when toggleButton is tapped. Checkout full source code on github.

Android Date Picker and Time Picker Example

Many times arises a need to take date and time input from user. Then starts a series of converting tedious date formats and shameless workarounds. This is a tutorial explaining how to use Date Picker and Time Picker in Android. It presents an aesthetic interface to user and provides data in standard format to programmer. Plus, its a good UI practice.

To present these dialogs, we will use DialogFragment to host them.

If you are a code-first tutorial-later person then you can have a look at sample project hosted on github.

Time Picker

To integrate Time Picker Dialog in your Activity you need to define an inner class like below.


public class TimePickerFragment extends DialogFragment implements
			TimePickerDialog.OnTimeSetListener {

		@Override
		public Dialog onCreateDialog(Bundle savedInstanceState) {
			// Use the current time as the default values for the picker
			final Calendar c = Calendar.getInstance();
			int hour = c.get(Calendar.HOUR_OF_DAY);
			int minute = c.get(Calendar.MINUTE);

			// Create a new instance of TimePickerDialog and return it
			return new TimePickerDialog(context, this, hour, minute,
					DateFormat.is24HourFormat(context));
		}

		public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
			// Do something with the time chosen by the user
			
		}
	}



onCreateDialog returns our TimePicker dialog, we are initializing it with device’s current time. onTimeSet is called when a time is selected in dialog. We can do whatever we want to do with that time.

We can show this TimePicker Dialog on any event such as button click using following code.

DialogFragment newFragment = new TimePickerFragment();
    newFragment.show(getSupportFragmentManager(), "timePicker");

Look closely in onTimeSet we have hourOfDay, that will be in 24 hour format. To convert it into 12 hour format and display AM/PM we need to tweak it a bit as show below.

	public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
String AM_PM = "";

			Calendar datetime = Calendar.getInstance();
			datetime.set(Calendar.HOUR_OF_DAY, hourOfDay);
			datetime.set(Calendar.MINUTE, minute);

			if (datetime.get(Calendar.AM_PM) == Calendar.AM)
				AM_PM = "AM";
			else if (datetime.get(Calendar.AM_PM) == Calendar.PM)
				AM_PM = "PM";

			String hours = (datetime.get(Calendar.HOUR) == 0) ? "12" : datetime
					.get(Calendar.HOUR) + "";
			
			Log.d(“Time: ”, hours + “ : ” + minute + “  ” + AM_PM );

}

Check out sample project hosted on github for full source code.

Date Picker

For using DatePicker, TimePicker like approach is used.

A class needs to be defined like below.

public class DatePickerFragment extends DialogFragment implements
			DatePickerDialog.OnDateSetListener {

		@Override
		public Dialog onCreateDialog(Bundle savedInstanceState) {
			// Use the current date as the default date in the picker
			final Calendar c = Calendar.getInstance();
			int year = c.get(Calendar.YEAR);
			int month = c.get(Calendar.MONTH);
			int day = c.get(Calendar.DAY_OF_MONTH);

			// Create a new instance of DatePickerDialog and return it
			return new DatePickerDialog(getActivity(), this, year, month, day);
		}

		public void onDateSet(DatePicker view, int year, int month, int day) {
			// Do something with the date chosen by the user

			/*
			 * MOnth returned from datepicker is always one less than actual
			 * value for example: December will be 11, January will be 0 and so
			 * on.
			 */

			month = month + 1;

			Log.d(“Date: ” , day + " - " + month + " - " + year); 

		}
	}


We can call it with following code.



DialogFragment newFragment = new DatePickerFragment();
newFragment.show(getFragmentManager(), "datePicker");


In onCreateDialog we intialize our dialog with device’s current date. onDateSet is called when a date is selected in dialog.

Note that in onDateSet, month returned is one less than the actual month so we need to increment it by 1.

Check out full source code of sample project at github.

See it in action:

Date and Time Picker Android