Showing posts with label Jelly Bean. Show all posts
Showing posts with label Jelly Bean. Show all posts

Wednesday, July 25, 2012

Tutorial on Android Voice recognition


You may have heard about the "Google Now project" where you give the voice command and Android fetches result for you. It recognizes your voice and converts it into the text or takes the appropriate action. Have you ever thought how is it done? If your answer is voice recognition API, then you are absolutly right. Recently while playing with Android voice recognition APIs, I found some interesting stuffs. APIs are really easy to use with application. Given below is a small tutorial on voice/speech recognition API. The final application will look similar to that of application shown below. The application may not work on the Android Emulator because it doesn't support voice recognition. But the same can work on the phone.


Android jelly bean voice recognition application prompt

Project Information: Meta-data about the project.

Platform Version : Android API Level 15.
IDE : Eclipse Helios Service Release 2
Emulator : Android 4.1(API 16)

Prerequisite: Preliminary knowledge of Android application framework, and Intent.

Voice recognition feature can be achieved by RecognizerIntent. Create an Intent of type RecognizerIntent and pass the extra parameters and start activity for the result. It basically starts the recognizer prompt customized by your extra parameters. Internally voice recognition communicates with the server and gets the results. So you must provide the internet access permission for the application. Android Jelly Bean(API level 16) doesn't  require internet connection to perform voice recognition.  Once the voice recognition is done, recognizer returns value in  onActivityResult() method parameters.
First create project by Eclipse > File> New Project>Android Application Project. The following dialog box will appear. Fill the required field, i.e Application Name, Project Name and Package. Now press the next button.


Once the dialog box appears, select the BlankActivity and click the next button.


Fill the Activity Name and Layout file name in the dialog box shown below and hit the finish button.
Android voice recognition project setup



This process will setup the basic project files. Now we are going to add four buttons in the activity_voice_recognition.xml file.  You can modify the layout file using either Graphical Layout editor or xml editor. The content of the file is shown below. As you may notice that we have attached speak() method with button in onClick tag. When the button gets clicked, the speak() method will get executed. We will define speak() in the main activity.


<LinearLayout 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:orientation="vertical" >

    <EditText
        android:id="@+id/etTextHint"
        android:gravity="top"
        android:inputType="textMultiLine"
        android:lines="1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:text="@string/etSearchHint"/>
    
    <Button
        android:id="@+id/btSpeak"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="speak"
        android:padding="@dimen/padding_medium"
        android:text="@string/btSpeak"
        tools:context=".VoiceRecognitionActivity" />

    <Spinner
        android:id="@+id/sNoOfMatches"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:entries="@array/saNoOfMatches"
        android:prompt="@string/sNoOfMatches"/>
    
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/tvTextMatches"
        android:textStyle="bold" />

    <ListView
        android:id="@+id/lvTextMatches"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
You may have noticed that the String constants are being accessed from the resource. Now add the string constants in string.xml. This file should look similar to the one shown below.
<resources>
    <string name="app_name">VoiceRecognitionExample</string>
    <string name="btSpeak">Speak</string>
    <string name="menu_settings">Settings</string>
    <string name="title_activity_voice_recognition">Voice Recognition</string>
    <string name="tvTextMatches">Text Matches</string>
    <string name="sNoOfMatches">No of Matches</string>
    <string name="etSearchHint">Speech hint here</string>
    <string-array name="saNoOfMatches">
        <item>1</item>
        <item>2</item>
        <item>3</item>
        <item>4</item>
        <item>5</item>
        <item>6</item>
        <item>7</item>
        <item>8</item>
        <item>9</item>
        <item>10</item>
    </string-array>
</resources> 
Now let's define the Activity class. This activity class, with the help of checkVoiceRecognition() method, will first check whether the Voice recognition is available or not. If voice recognition feature is not available, then toast a message and disable the button. Speak() method is defined here which gets called once the speak button is pressed. In this method we are creating RecognizerIntent and passing the extra parameters. The code below has embedded comments which makes it easy to understand.


package com.rakesh.voicerecognitionexample;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast;

public class VoiceRecognitionActivity extends Activity {
 private static final int VOICE_RECOGNITION_REQUEST_CODE = 1001;

 private EditText metTextHint;
 private ListView mlvTextMatches;
 private Spinner msTextMatches;
 private Button mbtSpeak;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_voice_recognition);
  metTextHint = (EditText) findViewById(R.id.etTextHint);
  mlvTextMatches = (ListView) findViewById(R.id.lvTextMatches);
  msTextMatches = (Spinner) findViewById(R.id.sNoOfMatches);
  mbtSpeak = (Button) findViewById(R.id.btSpeak);
  checkVoiceRecognition()
 }

 public void checkVoiceRecognition() {
  // Check if voice recognition is present
  PackageManager pm = getPackageManager();
  List activities = pm.queryIntentActivities(new Intent(
    RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
  if (activities.size() == 0) {
   mbtSpeak.setEnabled(false);
   mbtSpeak.setText("Voice recognizer not present")
   Toast.makeText(this, "Voice recognizer not present",
     Toast.LENGTH_SHORT).show();
  }
 }

 public void speak(View view) {
  Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);

  // Specify the calling package to identify your application
  intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, getClass()
    .getPackage().getName());

  // Display an hint to the user about what he should say.
  intent.putExtra(RecognizerIntent.EXTRA_PROMPT, metTextHint.getText()
    .toString());

  // Given an hint to the recognizer about what the user is going to say
  //There are two form of language model available
  //1.LANGUAGE_MODEL_WEB_SEARCH : For short phrases
  //2.LANGUAGE_MODEL_FREE_FORM  : If not sure about the words or phrases and its domain.
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
    RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);

  // If number of Matches is not selected then return show toast message
  if (msTextMatches.getSelectedItemPosition() == AdapterView.INVALID_POSITION) {
   Toast.makeText(this, "Please select No. of Matches from spinner",
     Toast.LENGTH_SHORT).show();
   return;
  }

  int noOfMatches = Integer.parseInt(msTextMatches.getSelectedItem()
    .toString());
  // Specify how many results you want to receive. The results will be
  // sorted where the first result is the one with higher confidence.
  intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, noOfMatches);
  //Start the Voice recognizer activity for the result.
  startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == VOICE_RECOGNITION_REQUEST_CODE)

   //If Voice recognition is successful then it returns RESULT_OK
   if(resultCode == RESULT_OK) {

    ArrayList textMatchList = data
    .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);

    if (!textMatchList.isEmpty()) {
     // If first Match contains the 'search' word
     // Then start web search.
     if (textMatchList.get(0).contains("search")) {

        String searchQuery = textMatchList.get(0);
                                           searchQuery = searchQuery.replace("search","");
        Intent search = new Intent(Intent.ACTION_WEB_SEARCH);
        search.putExtra(SearchManager.QUERY, searchQuery);
        startActivity(search);
     } else {
         // populate the Matches
         mlvTextMatches
      .setAdapter(new ArrayAdapter(this,
        android.R.layout.simple_list_item_1,
        textMatchList));
     }

    }
   //Result code for various error. 
   }else if(resultCode == RecognizerIntent.RESULT_AUDIO_ERROR){
    showToastMessage("Audio Error");
   }else if(resultCode == RecognizerIntent.RESULT_CLIENT_ERROR){
    showToastMessage("Client Error");
   }else if(resultCode == RecognizerIntent.RESULT_NETWORK_ERROR){
    showToastMessage("Network Error");
   }else if(resultCode == RecognizerIntent.RESULT_NO_MATCH){
    showToastMessage("No Match");
   }else if(resultCode == RecognizerIntent.RESULT_SERVER_ERROR){
    showToastMessage("Server Error");
   }
  super.onActivityResult(requestCode, resultCode, data);
 }
 /**
 * Helper method to show the toast message
 **/
 void showToastMessage(String message){
  Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 }
}

Here is the Android manifest file. You can see that  INTERNET permission has been provided to the application because of the voice recognizer's need to send the query to the server and get the result.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.rakesh.voicerecognitionexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <!-- Permissions -->
 <uses-permission android:name="android.permission.INTERNET" />
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
       
        <activity
            android:name=".VoiceRecognitionActivity"
            android:label="@string/title_activity_voice_recognition" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Once you are done with coding then connect the phone with your system and hit the run button on Eclipse IDE. Eclipse will install and launch the application. You will see the following activities on your device screen.
Web search using voice recognition
In the next tutorial, we will learn how to use the new voice recognition API introduced in Android Jelly Bean(API level 16) along with the examples.
If you are interested in the source code, then you can get it from github.

Other links:


Your valuable comments are always welcomed. It will help to improve my post and understanding.

"By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest."
By : Confucius

Tuesday, July 24, 2012

Where is the Android Jelly Bean Source code?

I have been looking for Android Jelly bean source code since its release. I found a lot of blogs on the source code release, but they hardly provided any pointer to get the Android 4.1 source code. Although Google has announced the release of the source code, on checking the Android website for source code, it still shows Android 4.0(Icecream sandwich). Seems that Google hasn't updated the web-page accordingly.  If you are wondering why am I interested so much in the source code then please go through this post "Android source code browsing". While I started my quest for Android source code, I found couple of resources from where you can explore/download the Android 4.1 source code.

Android source code browsing resources:

  • Android googlesource.com web site : This is the official website of Android source code. However they have not updated the download instructions. You can use same instructions given on the page except the "repo init" command. Use command below for "repo init".
    repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r3
    
  • Android xref :  This website has recently indexed the Android 4.1 source code. It has been developed to make Android source code browsing easier. It doesn't have as much functionanlities as that of grepcode.com, but it is still a good website(At the time of writing, grepcode has not indexed the Android 4.1 source code.).
  • Git hub repo: This is the github site which has the latest Android 4.1 source code. If you are interested in only downloading the source code then this is really a good one. If you are looking for code browsing, then this site is not as good as that of Android xref or grepcode.

Similar post:


Your valuable comments are always welcomed. It will help to improve my post and understanding.

"By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest."
By : Confucius

Tuesday, July 17, 2012

Tutorial on new Android Jelly Bean notification

You may have heard about Android Jelly Bean (API level 16). Google has improved a lot of features and introduced new features. One of them is the notification. Now they have made the notification more versatile by introducing media rich notification. Google has come up with three special style of notification which are mentioned below. Even developer can write his own customized notification style using remote view.The old Notification class constructor has been deprecated and a brand new and enhanced version of Notification has been introduced.

Notification Type:

  • Basic Notification - Shows simple and short notification with icon.
  • Big Picture Notification - Shows visual content such as bitmap.
  • Big Text Notification - Shows multiline Textview object.
  • Inbox Style Notification - Shows any kind of list, e.g messages, headline etc. 

Old syntax requires us to create an object of notification but now Android uses builder patter to create the notification object. Notification.Builder class has been introduced to make this task easier. This class returns the builder object which is configurable according to your requirements. The helper classes have been introduced like Notification.BigPictureStyle, Notification.BigTextStyle, and Notification.InboxStyle. These classes are re-builder classes which take object created by Notification.Builder class and  modify the behavior like so.

Project Information: Meta-data about the project.

Platform Version : Android API Level 16.
IDE : Eclipse Helios Service Release 2
Emulator : Android 4.1(API 16)

Prerequisite: Preliminary knowledge of Android application framework, and Intent.

First create project by Eclipse > File> New Project>Android Application Project. The following dialog box will appear. Fill the required field, i.e Application Name, Project Name and Package Name. Don't forget to select the Build SDK version (for this tutorial Google API 16 has been selected). Now press the next button.

 Once the dialog box appears, select the BlankActivity and click the next button.



 Fill the Activity Name and Layout file name for the dialog box shown below and hit the finish button.


This process will setup the basic project files. Now we are going to add four buttons in the activity_main.xml file.  You can modify the layout file using either Graphical Layout editor or xml editor. The content of the file should look like this.

<LinearLayout 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:orientation="vertical"
    android:gravity="center_horizontal">

    <Button
        android:id="@+id/btBasicNotification"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal|center_vertical"
        android:onClick="sendBasicNotification"
        android:text="@string/btBasicNotification" 
        android:background="@drawable/button_background"
        android:textColor="#000000"
        />
    <Button
        android:id="@+id/btBigTextNotification"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal|center_vertical"
        android:onClick="sendBigTextStyleNotification"
        android:text="@string/btBigTextNotification" 
        android:background="@drawable/button_background"
        android:textColor="#000000"
        />
   <Button
        android:id="@+id/btBigPictureNotification"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal|center_vertical"
        android:onClick="sendBigPictureStyleNotification"
        android:text="@string/btBigPictureNotification"
        android:background="@drawable/button_background"
        android:textColor="#000000" />
   <Button
        android:id="@+id/btInboxStyleNotification"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal|center_vertical"
        android:onClick="sendInboxStyleNotification"
        android:text="@string/btInboxStyleNotification"
        android:background="@drawable/button_background"
        android:textColor="#000000"/>
</LinearLayout>

You may have noticed that onClick methods are associated with respective buttons. If you don't know how to define and use the background file for view then ignore the android:background field. Now we are going to define the methods sendBasicNotification, sendBigTextStyleNotification, sendBigPictureStyleNotification and sendInboxStyleNotification. As the method name suggests, it sends that particular kind of notification. In each method we are creating Notification.Builder object, and customizing the object. Here builder pattern has been used to customize the object. Once the customization is done,  call build() method to get the notification object. In this new notification system, at most three actions can be associated to a notification, which are displayed below the notification content.  This can be achieved by calling addAction() method on the builder object. The same number of icons you will see on the notification as you will notice for sendBigPictureStyleNotifcation() method. Notification priority can also be set by calling setPriority() method as shown in sendBigTextStyleNotification() method. In the code given below, intent has been used to invoke the HandleNotificationActivity.

package com.example.jellybeannotificationexample;

import android.app.Activity;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;

public class NotificationMainActivity extends Activity {

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

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.activity_main, menu);
  return true;
 }

 public void sendBasicNotification(View view) {
  Notification notification = new Notification.Builder(this)
    .setContentTitle("Basic Notification")
    .setContentText("Basic Notification, used earlier")
    .setSmallIcon(R.drawable.ic_launcher_share).build();
  notification.flags |= Notification.FLAG_AUTO_CANCEL;
  NotificationManager notificationManager = getNotificationManager();
  notificationManager.notify(0, notification);
 }

 public void sendBigTextStyleNotification(View view) {
  String msgText = "Jeally Bean Notification example!! "
    + "where you will see three different kind of notification. "
    + "you can even put the very long string here.";

  NotificationManager notificationManager = getNotificationManager();
  PendingIntent pi = getPendingIntent();
  Builder builder = new Notification.Builder(this);
  builder.setContentTitle("Big text Notofication")
    .setContentText("Big text Notification")
    .setSmallIcon(R.drawable.ic_launcher)
    .setAutoCancel(true);
    .setPriority(Notification.PRIORITY_HIGH)
    .addAction(R.drawable.ic_launcher_web, "show activity", pi);
  Notification notification = new Notification.BigTextStyle(builder)
    .bigText(msgText).build();
 
  notificationManager.notify(0, notification);
 }

 public void sendBigPictureStyleNotification(View view) {
  PendingIntent pi = getPendingIntent();
  Builder builder = new Notification.Builder(this);
  builder.setContentTitle("BP notification")
    // Notification title
    .setContentText("BigPicutre notification")
    // you can put subject line.
    .setSmallIcon(R.drawable.ic_launcher)
    // Set your notification icon here.
    .addAction(R.drawable.ic_launcher_web, "show activity", pi)
    .addAction(
      R.drawable.ic_launcher_share,
      "Share",
      PendingIntent.getActivity(getApplicationContext(), 0,
        getIntent(), 0, null));

  // Now create the Big picture notification.
  Notification notification = new Notification.BigPictureStyle(builder)
    .bigPicture(
      BitmapFactory.decodeResource(getResources(),
        R.drawable.big_picture)).build();
  // Put the auto cancel notification flag
  notification.flags |= Notification.FLAG_AUTO_CANCEL;
  NotificationManager notificationManager = getNotificationManager();
  notificationManager.notify(0, notification);
 }

 public void sendInboxStyleNotification(View view) {
  PendingIntent pi = getPendingIntent();
  Builder builder = new Notification.Builder(this)
    .setContentTitle("IS Notification")
    .setContentText("Inbox Style notification!!")
    .setSmallIcon(R.drawable.ic_launcher)
    .addAction(R.drawable.ic_launcher_web, "show activity", pi);

  Notification notification = new Notification.InboxStyle(builder)
    .addLine("First message").addLine("Second message")
    .addLine("Thrid message").addLine("Fourth Message")
    .setSummaryText("+2 more").build();
  // Put the auto cancel notification flag
  notification.flags |= Notification.FLAG_AUTO_CANCEL;
  NotificationManager notificationManager = getNotificationManager();
  notificationManager.notify(0, notification);
 }

 public PendingIntent getPendingIntent() {
  return PendingIntent.getActivity(this, 0, new Intent(this,
    HandleNotificationActivity.class), 0);
 }

 public NotificationManager getNotificationManager() {
  return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
 }
}


We have defined basic HandleNotificationActivity which just shows a simple message when intent is fired for this activity. The content of the file is as following.
package com.example.jellybeannotificationexample;

import android.app.Activity;
import android.os.Bundle;

public class HandleNotificationActivity extends Activity {

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

The corresponding layout file(handle_notification_activity.xml) is given below
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
     android:gravity="center_horizontal|center_vertical" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tvHandleNotification"
        android:textSize="20dp"
        android:textStyle="bold|italic" />

</LinearLayout>

Now you have to define the Android manifiest file. HandleNotificationActivity should be included in the manifest file and then put the intent filter for this activity.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.jellybeannotificationexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="16" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name=".NotificationMainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".HandleNotificationActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


Once you are done with the coding, just execute it. You will see the application as shown in the picture below. On clicking the button you will see the corresponding notification on the upper part of the screen. If you drag the notification down then you can see the entire message and corresponding icon. The  pictures below are the notification when they were dragged down.
jelly bean media rich notification application
Application
Andriod Jelly bean (API 16) Basic notification
Basic notification
Andriod 4.1 (API 16) Big Text style notification
Big Text Style
Andriod 4.1 Jelly bean (API 16) Big picture style notification
Big Picture Style
Andriod 4.1 Jelly bean (API 16) Inbox style notification
Inbox Style

If you are interested to know more about source code, you can find it over here.

Your valuable comments are always welcomed. It will help to improve my post and understanding.

"By three methods we may learn wisdom: First, by reflection, which is noblest; Second, by imitation, which is easiest; and third by experience, which is the bitterest."
By : Confucius