Saturday, October 30, 2010

The Speech App using XML Layout

In our previous example we created a Text to Speech app in which we created an instance of the view object and passed it to the on-create-view macro. In this example we will use the android XML layout facility. You will see how simple our apps become.

First edit the "res/layout/main.xml" file to read as follows.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_height="fill_parent"
  android:layout_width="fill_parent"
  android:orientation="vertical"
  android:name="main">
  <TextView android:text="@string/label_text"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"/>
  <EditText android:id="@+id/edit_text"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"/>
  <Button android:text="@string/button_text"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:onClick="onSpeakButtonClick"/>
</LinearLayout>

Notice the second to last line. We are setting the onClickListener of the button to a public method in the activity class called "onSpeakButtonClick". Next edit the "res/values/strings.xml" file to read as follows.


<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="app_name">hello</string>
  <string name="label_text">Enter the text to speak here</string>
  <string name="button_text">Speak!</string>
</resources>

Now let us look at the source code. Edit your "src/kawa/android/hello.scm" file to read as follows.


(require 'android-defs)

(activity hello
  (mTts ::android.speech.tts.TextToSpeech)
  (on-create
    ((this):setContentView kawa.android.R$layout:main)
    (set! mTts
      (android.speech.tts.TextToSpeech
        (this)
        (lambda (i) ()))))
  ((onSpeechButtonClick (v ::android.view.View)) ::void
    (mTts:speak
      ((as <android.widget.EditText>
        ((this):findViewById
          kawa.android.R$id:edit_text)):getText)
      android.speech.tts.TextToSpeech:QUEUE_FLUSH
      #!null)))

First we create the android.app.Activity class using the 'activity' macro, with the name 'hello'.  Inside the hello class we define an instance variable and two methods. First we define the instance variable mTTs. Next we use the 'create-view' macro to create the classes 'onCreate' method. Here, we did not use the 'on-create-view' macro as we did in the previous examples. Thats because we want to call the 'setContentView' method ourselves.
 ((this):setContentView kawa.android.R$layout:main)
In the line above we 'setContentView' to 'kawa.speaker.R$layout:main'. We have to use the full qualified package name. We use the '$' in 'R$layout' because that is how inner classes are generated by the compiler.

If you noticed we did not call the super class onCreate method ourselves. This is called behind the scenes by the 'on-create' macro. This is one of the advantages in developing with scheme. We can simplify coding using macros. We then set! the mTts instance variable to a new instance of the android.speech.tts.TextToSpeech class.

We don't have a macro to create the onSpeakButtonClick method for us. So we define the method itself here. The rest of the code is the same as the one we saw in the previous example.

Now build your app and run it.

Monday, October 25, 2010

A Text to Speech App

Further to our introductory example here I will show you a Text to Speech app. The app will speak out whatever you type into the edit box. Here is the source code.

(require 'android-defs)


(activity hello
  (on-create-view
    (define mTts
      (android.speech.tts.TextToSpeech
        (this)
        (lambda (i) ())))
    (android.widget.LinearLayout (this)
      orientation: android.widget.LinearLayout:VERTICAL
      view:
        (android.widget.TextView (this)
          text: "Enter the text to speak")
      view:
        (android.widget.EditText (this)
          id: 101)
      view:
        (android.widget.Button (this)
          text: "Speak!"
          on-click-listener:
            (lambda (v)
              (mTts:speak
                ((as <android.widget.EditText>
                  ((this):findViewById 101)):getText)
                android.speech.tts.TextToSpeech:QUEUE_FLUSH
                #!null))))))


Copy and paste the code above into your hello.scm file and build the app.

In the on-create-view macro we first define an instance of the android.speech.tts.TextToSpeech class and we assign it to variable mTts. The TextToSpeech class takes two arguments in its constructor.  The first argument is the context '(this)'. the second argument is an instance of the TextToSpeech.OnInitListener class. We simply pass and anonymous empty function to it and Kawa will take care of the rest.

Then we add an android.widget.EditText instance to our layout and we give it an id. In the buttons onClickListener we call the speak method of the mTts variable.

              (mTts:speak
                ((as <android.widget.EditText>
                  ((this):findViewById 101)):getText)
                android.speech.tts.TextToSpeech:QUEUE_FLUSH
                #!null))))))
Here we pass it three arguments. The first argument is the text you typed in the text box. We get hold of it with it's id and call its getText method. The second argument is the queue type and the last argument is a null.

Introduction to Android App Development in Scheme

This tutorial is based on the Kawa - Scheme Language Framework developed by Per Bothner. Kawa Scheme is used by Google's AppInventor framework. However Appinventor only allows you to develop using a visual interface. The tutorials here will show you how to develop Android Apps by writing code in Scheme. So let us jump into it straight away.

Here is the source code for a simple app that will display a TextView and show a Button. When you click the button it will display a Toast message.

(require 'android-defs)


(activity hello
  (on-create-view
    (android.widget.LinearLayout (this)
      orientation: android.widget.LinearLayout:VERTICAL
      view:
        (android.widget.TextView (this)
          text: "Hello Android, from Kawa Android!")
      view:
        (android.widget.Button (this)
          text: "Click Me!"
          on-click-listener:
            (lambda (v)
              ((android.widget.Toast:makeText
                (this)
                "Beep Bop! You clicked me!"
                android.widget.Toast:LENGTH_LONG):show))))))

You can see how simple and functional the whole code is. Even if you don't know scheme you can figure what is going on.

To get this code working on linux you will need to follow Per Bothner's blog post on how to set up your android development environment. Make sure you use the latest svn version of Kawa. For windows users you can follow the same equivalent procedure and it works. Get the hello example shown at the site working for you first. Copy and paste the code above into your hello.scm file and create the app.

Now for some explanation of the code. First we load the android-defs library, which loads some android definitions.
(require 'android-defs)

From Kawa Scheme we can interact with java virtual machine directly. Next we create an scheme expression to call the 'activity' macro. Scheme expressions have the form
(function & args)
Our expression is
(activity hello (....))
The activity macro creates an android.app.Activity with its first argument as name, which in this case is 'hello', and the second argument is an expression that will evaluate to an android.view.View object. The second argument in this case is the expression
(on-create-view ...)
which is also a macro that evaluates its argument expression and calls the setContentView method of the activity with the evaluated view. The rest of the code is for generating the view object.

In Kawa we can instantiate an instance of a class with this syntax

(MyClass keyword1: value1 ... kewordN: valueN)

where keyword corresponds to a field or method name. In the case of a field it will set the value of the field, and in case of a method it will call the method with the value.

In case you pass a lambda expression (anonymous function) as a value then Kawa will call the method with a corresponding anonymous class.

Also keywords need not match the method names exactly. Keyword 'text' will make the compiler look for the "setText" "addText" methods. "on-click-listener" gets translated to "onClickListener".

So this is what happens in the rest of the code. First we create an instance of the LinearLayout
android.widget.LinearLayout (this)
to which we pass keyword value pairs. The first keyword sets the orientation. Then we call 'view' twice (Kawa will call the addView method of LinearLayout) passing it the TextView instance the first time and the Button instance the second time.

We create the TextView and Button object similarly. The on-click-listener of the Button object is passed a Lambda function which in turn instantiates a Toast object using its static method makeText. Kawa will automagically create the android.view.View.OnClickListener class as required by the onClickListener function of the button.

Now let us look at this part of the code.

              ((android.widget.Toast:makeText
                (this)
                "Beep Bop! You clicked me!"
                android.widget.Toast:LENGTH_LONG):show))))))

First we call the makeText static method of the android.widget.Toast class which returns an instance of the Toast class and we immediately call its ':show' method. In kawa to access a field or method of an object use the ':' notation.