Skip to content

Gorilla for Android (GUI)

zdia edited this page Mar 9, 2012 · 19 revisions

David Welton’s Hecl will also grant access to the Android’s GUI. Although it is recommended to develop GUI widgets by help of XML definition files we will follow the direct command way because it is easier to push just the changes to a single script file then to compile and install the whole application: The XML sources have to be compiled in.

Basic steps:

Start the emulator

emulator -avd <myimage>

To continue we unlock Android with pressing F2.

Reading the sdcard

To get read access for the sdcard contents mount the sdcard locally:

# mount -o loop sdcard.img /mnt

Preparing Hecl-debug.apk to read script files on the scdard

Be sure that res/raw/script.hcl in Hecl.apk will have this content:

source /sdcard/gorillagui.hcl

Note: The source command is not available at the master branch. Choose the branch android on the fork
https://github.com/zdia/hecl to get the patched source code files.

Unfortunately the procedure recommended in android/readme to modify the Hecl.apk by just modifying it by a zip command does not work:

$ zip -r bin/Hecl-debug.apk res/raw/script.hcl
adding: res/raw/script.hcl (stored 0%)
$ adb install Hecl-debug.apk
1559 KB/s (908591 bytes in 0.569s)
pkg: /data/local/tmp/Hecl-debug.apk
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]

adb logcat shows:

Package org.hecl.android has no certificates at entry res/raw/script.hcl; ignoring!

So we have to recompile the Hecl.apk with the modified script.hcl (which finally will be our application script …)

ant android-reinstall

Writing and testing the code

From now on it will be enough to just edit the Gorilla script on the sdcard repeating these steps:

  • edit the gorillagui.hcl script
  • put it to the sdcard with: adb push gorillagui.hcl /script/gorillagui.hcl
  • launch Hecl and see the results

Debugging

The best way to get information about how the application works is to dedicate a console terminal to the continuous logcat output:

adb logcat

Not only the Hecl command androidlog will show its results but all the System.out.println(“message”); and puts $string commands can be seen in this way.

Force stop the application

Testing the callbacks is a bit tedious because they will stay unchanged after a restart. You have to select the Force stop menu to clear the memory. Or try to kill the pid:

adb shell
# ps
# kill -9 <pid>

Display at Launch Time

For high resolution a 72×72.png format is recommended. If the icon is to be found e.g. as res/drawable/gorilla72.png you insert the icon name without extension:

 <application android:icon="@org.hecl.android:drawable/gorilla72">

The launch name will be given by android:label:

   <activity android:name="org.hecl.android.Hecl" android:label="Password Gorilla"

Layout implementation

Managing activities

At present (March 2012, version 0.9) there is only one sublevel available, i.e.
AndroidManifest.xml defines just the two acticities Hecl and Subhecl. The first call of

set context [activity]

will save the context pointer to the main interpreter Hecl whereas

newActivity $parentcontext $code

will force the interpreter subhecl to interpret $code inside a new activity.

But our application may need more activities, i.e. more screens.

The following steps will enable us to create our own activities by using subhecls:

1) Every Android activity has to be declared in the file AndroidManifest.xml before it can be used by the application code. The generic layout for activities FirstActivity, SecondActivity and so on would therefore look somethink like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="org.hecl.android"
    ...
    <activity android:name="org.hecl.android.Hecl" android:label="Hecl"
      android:configChanges="orientation|keyboardHidden">
      <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

    <activity android:name="org.hecl.android.FirstActivity" android:label="FirstActivity"
      android:configChanges="orientation|keyboardHidden">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
      </intent-filter>
    </activity>

    <activity android:name="org.hecl.android.SecondActivity" android:label="SecondActivity"
      android:configChanges="orientation|keyboardHidden">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
      </intent-filter>
    </activity>
    ...
</manifest>

2) In order to get access to this activities in a flexible way the procedure newActivity in the file res/raw/lib.hcl has to be modified:

proc newActivity {context code} {
    set h [subhecl -new [list]]
    set intent [intent -new [list]]
    $intent setclass $context [$h getclass]
    $h setmailbox $code
    $context startActivity $intent
}

This procedure will get an additional parameter name and will be modified to:

proc newActivity { name context code} {
  # name -- name of activity like declared in AndroidManifest.xml
  # context -- oldcontext of parent activity
  # code -- code to be executed, in general a procedure

    set h [$name -new [list]]
    set intent [intent -new [list]]
    $intent setclass $context [$h getclass]
    $h setmailbox $code
    $context startActivity $intent
}

An activity defined in the AndroidManifest.xml can now be called by this command:

newActivity firstactivity $oldcontext $code

To work like before the original res/raw/script.hcl has then to be changed in line 628 from:

newActivity $context $procname

to:

newActivity subhecl $context $procname

3) We still have to create a Java class FirstActivity and let the Hecl interpreter know that there exists a command firstactivity.

For this purpose just copy the file hecl/android/src/org/hecl/android/SubHecl.java to FirstActivity.java and change the line

public class SubHecl extends Hecl

to:

public class FirstActivity extends Hecl

4) Finally we load this class into the Hecl interpreter. We add our FirstActivity.class under the name firstactivity to Hecl.java:

   protected void createCommands(Interp i) throws HeclException {
     JavaCmd.load(interp, "org.hecl.android.Hecl", "hecl");
     JavaCmd.load(interp, "org.hecl.android.SubHecl", "subhecl");
     JavaCmd.load(interp, "org.hecl.android.FirstActivity", "firstactivity"
    }

5) So Hecl will know now the new command firstactivity and after having recompiled Hecl for Android we can add our first activity to Android’s activity stack:

newActivity firstactivity $oldcontext $code

Layout

The layout will be defined in general by lines like these:

  set layoutparams [linearlayoutparams -new {FILL_PARENT WRAP_CONTENT}] 
  set myActivityLayout [linearlayout -new $context -layoutparams $layoutparams]
  $myActivityLayout addview $myView
  ...

Screen #1: OpenDialog

The first activity will be to create a screen with a text line and three buttons. The skeleton file can be found under the branch android at https://github.com/zdia/hecl/blob/android/projects/PWGorilla/gorillagui.hcl:

proc openDialog {} {
  # screen #1: Open - New - Exit
  
  set context [activity]
  set layoutparams [linearlayoutparams -new {FILL_PARENT WRAP_CONTENT}]
  
  set openDialogLayout [linearlayout -new $context -layoutparams $layoutparams]
  $openDialogLayout setorientation VERTICAL
  # Note: view orientation can be changed by click
  # better to provide also horizontal orientation
 
  $openDialogLayout addview [textview -new $context -text "\n Select a task:\n" \
    -layoutparams $layoutparams -textsize 14.0]
  $openDialogLayout addview [button -new $context -text "Open" \
    -layoutparams $layoutparams]
  $openDialogLayout addview [button -new $context -text "New" \
    -layoutparams $layoutparams]
  $openDialogLayout addview [button -new $context -text "Exit" \
    -layoutparams $layoutparams]
         
 [activity] setcontentview $openDialogLayout

}

proc main {} {
  global context
  
  [activity] settitle "Password Gorilla"
  
  openDialog
 
}

main

And here is a screenshot of the result:

Adding the button callbacks

Define a callback procedure which will be called by a button click. In this example it will just return the passed name of the button.

proc openCallback { option button } {
    androidlog "you pressed option: $option"
}

Then send the callback to the onClickListener method of the button object, e.g.:


  set exitButton [button -new $context -text "Exit" \
    -layoutparams $layoutparams]
    
  $exitButton setonclicklistener [callback -new [list [ list openCallback "exit" ]]]

Note: In Hecl you can code it also in this way:


  set exitButton [button -new $context -text "Exit" \
    -layoutparams $layoutparams \
    -onclicklistener [callback -new [list [ list openCallback "exit" ]]]
  ]

Alert

The command androidlog is good for the developer but the user will prefer an alert:

proc openCallback { option button } {
  alert "You have pressed option: $option"
}

which is based on the Android Toast widget and will look like that:

Screen #2: File Selection

Unfortunately there is no Android API for a file dialog but Hecl offers different file.* commands. To get the filenames in the folder /sdcard we can code:

  set path "/sdcard"
  file.cd $path
  set fileNames [file.list "./"]

For the presentation of the filenames we will use the Android listview widget with the Hecl command basiclist which will be populated with our list of filenames:

 set context [activity]
  [activity] settitle "Password Gorilla - Select Database"
  
  set layoutparams [linearlayoutparams -new {FILL_PARENT WRAP_CONTENT} ]
  set filesLayout [linearlayout -new $context -layoutparams $layoutparams]
  $filesLayout setorientation VERTICAL

  set filesListview [basiclist $context $fileNames \
    -layoutparams $layoutparams]
  $filesListview requestfocus
  
  set tv [textview -new $context \
       -text " Path: $path" \
       -layoutparams $layoutparams -textsize 12.0 ]
  $tv setTypeface 1 1 ;# Note: settypeface will cause error!
  $tv setTextColor -256 ;# yellow
  # Note: background in textview can only be set by XML definition file
  
  $filesLayout addview $tv
  $filesLayout addview $filesListview

  [activity] setcontentview $filesLayout

The result looks like this:

To answer to the user’s click action we still have to add a callback procedure which will open a clicked database item:

proc openDB { args } {
  # listview textview positionId rowId
  puts "openDB args: $args"
}

The parameter positionId will allow us to index the selected file in our list fileNames.

For our layout part filesListview which we had defined with Hecl’s Android command basiclist we can install the callback in this way:

  set selectFileCallback [callback -new [list [list openDB]]]
  $filesListview setonitemclicklistener $selectFileCallback

Note: The Hecl Android command basiclist uses a static arrayadapter and therefore it will not be possible to clear the passed list and add new ones. If you want dynamic arrayadapters which can be modified e.g. inside a callback you have to construct the adapter without a list:

set filesAdapter [arrayadapter -new \
		[list $context [reslookup R.layout.list_item] ] ]

The adapter has to be populated afterwards, e.g.:

 foreach file $fileNames {
    $filesAdapter add $file
  }  

Now it will be possible to clear it and repopulate it:

$filesAdapter clear
$filesAdapter add "newitem"

will be continued …