-
Notifications
You must be signed in to change notification settings - Fork 61
Gorilla for Android (GUI)
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.
emulator -avd <myimage>
To continue we unlock Android with pressing F2.
To get read access for the sdcard contents mount the sdcard locally:
# mount -o loop sdcard.img /mnt
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
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
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.
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>
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"
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
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
...
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:
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" ]]]
]
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:
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 …