Professional Documents
Culture Documents
Android y Basic
Android y Basic
Tutorials
http://www.basic4android.com
Feedback: support@basic4ppc.com
Updated: March 23, 2011
Table of contents
Installing Basic4android and Android SDK .....................................................................4
Hello world - Installing Android Emulator ......................................................................7
Guess my number - Visual designer & Events ............................................................... 13
IDE Tips ....................................................................................................................... 19
B4A-HelpViewer - View and search the documentation offline..................................... 24
B4A-Bridge a new way to connect to your device ......................................................... 25
Connecting your device to the IDE using ADB.............................................................. 28
Android Process and activities life cycle........................................................................ 32
Static Code Modules ..................................................................................................... 36
Service Modules............................................................................................................ 37
Variables & Objects ...................................................................................................... 41
Currency Converter libraries, file manager and other important concepts.................... 45
Working with files......................................................................................................... 53
Views (controls) and Dialogs ........................................................................................ 57
GPS tutorial .................................................................................................................. 64
MediaPlayer Playing music ........................................................................................ 68
ListView tutorial ........................................................................................................... 71
ScrollView tutorial ........................................................................................................ 76
TabHost tutorial ............................................................................................................ 78
FlickrViewer example Download multiple images concurrently ................................. 80
Two activities example.................................................................................................. 81
Building a linked list using Type keyword..................................................................... 83
SQL tutorial .................................................................................................................. 85
XML parsing with the XMLSax library......................................................................... 90
Take pictures with the internal camera........................................................................... 93
Serial (Bluetooth) tutorial.............................................................................................. 95
JSON parsing and generating ...................................................................................... 100
Animation tutorial ....................................................................................................... 103
Network tutorial .......................................................................................................... 107
Regular Expressions .................................................................................................... 111
Downloading files using Services................................................................................ 114
AsyncStreams tutorial ................................................................................................. 116
Installation instructions:
The first step should be to install the Java JDK as Android SDK requires it as well.
Note that there is no problem with having several versions of Java installed on the same
computer.
- Open the Java 6 JDK download link.
- Select "Windows" in the platform combo box (for 64 bit machines as well). Android
SDK doesn't work with Java 64bit JDK. You should install the regular JDK for
64bit computers as well.
- Press on the red Continue button. There is no need to sign in!
If for some reason you don't see the red Continue button try to switch to a different
browser.
- On the next page you should press on the file link:
- Choose New and fill the fields similar to the following image:
- You will see several windows popping up and disappearing. This is fine.
- The emulator should boot up:
Wait... on the first time it can take several minutes till the emulator is ready.
The emulator is ready when it gets to this screen:
You may see this screen, which is the lock screen, instead:
10
11
There are two "Hello world!" messages in the screenshot as I ran the program
twice.
Unchecking "Filter" will show all available messages (not just messages relevant
to your program).
Hello world
12
13
The visual component, as it names suggests, displays the layout. It also allows you to
move and resize the views (controls).
Changing the layout in the visual component will also change the values stored in the
control panel.
Note that all the data is stored in the control panel component. Therefore nothing bad will
happen if the emulator crashes or is turned off.
You can connect to it again and the layout will appear.
The first step is to connect to the device. Press Tools - Connect.
This step takes several seconds. Note that the IDE will stay connected until the IDE
closes. Closing the designer will not disconnect the connection.
Use the Add View menu to add a Button, an EditText and a Label.
Change the views Text property and position them similar to this:
14
15
Code:
Sub Process_Globals
End Sub
Sub Globals
Dim MyNumber As Int
Dim EditText1 As EditText 'will hold a reference to the view added
by the designer
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("Layout1") 'Load the layout file.
MyNumber = Rnd(1, 100) 'Choose a random number between 1 to 99
End Sub
Sub Button1_Click
If EditText1.Text > MyNumber Then
ToastMessageShow("My number is smaller.", False)
Else If EditText1.Text < MyNumber Then
ToastMessageShow("My number is larger.", False)
Else
ToastMessageShow("Well done.", True)
End If
EditText1.SelectAll
End Sub
Some notes:
- Every activity module comes with an Activity object that you can use to access the
activity.
- Activity.LoadLayout loads the layout file.
- There are other views that can load layout files. Like Panel and TabHost. For
TabHost each tab page can be created by loading a layout file.
- In order to access a view that was added with the designer we need to declare it under
Sub Globals.
- ToastMessageShow shows a short message that disappears after a short period.
Using a toast message in this case is not optimal as it may be unnoticed when the soft
keyboard is open.
Tip for writing events subs:
In the IDE, write Sub and press space. You should see a small tooltip saying "press tab to
insert event declaration".
Press tab, choose the object type and choose the event.
16
Now all you need to do is to write the required event name and press enter.
Supporting multiple screen resolutions and orientations
Each layout file can include a number of layout variants. Each layout variant holds a
different set of values for the position and size of the views.
If for example, you change the text of any view it will be changed in all variants
automatically. However if you change the position of a view it will only affect the current
variant.
Note that scaling is handled automatically if required. Which means that if we run our
program on a high resolution device, the layout will be automatically scaled.
Still you may choose to create different variants for different scales.
When you load a layout file the variant that best matches the current device will be
loaded.
Usually you will need two variants:
- 320 x 480, scale = 1 (160 dpi). This is the default scale in portrait mode.
- 480 x 320, scale = 1 (160 dpi). Default scale in landscape mode.
Ok, so open the designer again. Load Layout1 file if it is not opened.
Choose "New Variant" and choose 480 x 320 (second option).
Change the emulator orientation by clicking on the emulator and then press on Ctrl +
F11.
Note that the device layout details appear under the list of variants.
Change the layout to be similar to this:
17
You can change the current selected variant and see how it affects the visual layout.
Save the layout and run the program.
Change the emulator orientation and see how the layout changes accordingly.
Android destroys the old activity and creates a new activity each time the orientation
changes. Therefore Activity.LoadLayout will be called again each time. Unfortunately
the number will also be randomly chosen again each time. This can be easily fixed... But
not in this tutorial.
The project is attached.
Last tip for this tutorial:
- The IDE includes an "Export as zip" option under Files menu. This method creates a zip
file with all the required files.
18
IDE Tips
The IDE has several powerful features which can help you concentrate on writing your
code and building your application.
I'm listing here some of the less obvious features:
- Ctrl
Pressing Ctrl + Space activates the auto complete feature which will then show you a list
with the available keywords, variables, modules, methods, subs, properties and fields.
The list includes a description for most items.
Pressing Ctrl + Space after typing the first few letters will usually select the required item
automatically.
- Tool tip information - while writing methods parameters, a tool tip will be opened with
the method signature and description.
The tool tip might hide some important code that you now need.
You can hide it by pressing escape. You can also turn it almost invisible by pressing the
ctrl key. Another press will return it to be fully opaque.
19
LogCat - The LogCat tab displays the device built-in logs. These logs are very useful for
debugging. You can log messages with the Log keyword.
In order to start displaying the logs you press on the Connect button.
The logs can be filtered and then you only see messages generated by Basic4android or
your application.
Note that if you have more than one device connected you can switch to a different
device by pressing on the Connect button.
Designer generated members tool - This tool allows you to add the declaration code for
the designer views and to add event subs.
Note that you only need to declare views that you intend to access by code.
Nothing will happen if you select an existing item (there will be no duplicated code).
20
To open this tool choose - Tools - Generate Members (from the designer form).
Background compilation - Pressing Alt + 3 will compile and install your application
while keeping the IDE responsive. The status bar at the bottom of the screen displays the
progress of the process and when the installation is completed.
A short sound will notify you if the process failed. In that case you may need to compile
regularly (F5) in order to see the error message (it depends on the type of error).
Working with multiple connected devices - In many cases you have more than one
device connected. For any operation that starts a connection you will be shown the list of
connected device and you will choose the target device.
If you compile in the background the last device will be used again. This is usually more
convenient than compiling in the foreground and selecting the target device each time.
Designer - Duplicate - You can duplicate any view by selecting the view and then
choosing Tools - Duplicate View. If the view has child views then all its child views will
be duplicated as well.
Export as zip - Export as zip option creates a zip file with all the required project files.
This is useful when you want to share your project with others or create a backup. It is
located under Files menu.
Clean Project / Clean Unused Files - Clean project deletes all generated files. These are
21
A list will be displayed with all the available types (that have at least one event). Choose
the required type and press enter.
22
The EventName string will be selected. Change it to match the object "EventName" value
and press enter. That's it.
Designer top most property - The designer has a "top most" check box which you can
use to keep the designer as the top most form. This is useful when working with the
designer and the emulator on a small screen.
Debugging data - By default Basic4android compiler adds some debugging data to your
code.
This data is used when an error occurs. It allows the program to show the original code
line which raised the error.
This data does take some space and may impact performance, though it usually should be
insignificant. You can remove this data by unchecking Project - Include Debug
Information.
23
For now there is no installation step. You should unzip the file and run B4A-HelpViewer.
Note that it should not be located under Program Files as it needs "write permissions".
Download link
24
25
That's it.
When B4A-Bridge gets connected it first checks if the designer application needs to be
updated. In that case it will first install the designer application.
B4A-Bridge keeps running as a service until you press on the Stop button.
You can always reach it by opening the notifications screen:
26
In the above dialog you should choose Open to start the application.
If you try to install an existing application signed with a different key, the install will
fail (without any meaningful message). You should first uninstall the existing
application. Go to the home screen - Settings - Applications - Manage applications choose the application - Uninstall.
Once you finished developing you should press on the Stop button in order to save
battery.
Note that B4A-Bridge was written with Basic4android.
The source code is available here:
http://www.basic4ppc.com/forum/basic...html#post45854
27
28
29
Many Android devices, but not all like some cheap Chinese Android tablets, have two
more USB interfaces "Android ADB Interface" and "Android Composite ADB
Interface". These belong to a device that, on successful USB driver installation, appears
as "Android Phone" in Device Manager and is used by adb to connect to the phone over
USB. If your phone lacks these it may still be possible to connect adb to it by means of
wireless as described above.
These Android interfaces, unlike SD card access, need custom USB drivers that are
located in the <android-sdk-windows>\usb_driver. In this folder there is a
android_winusb.inf file that contains the USB IDs needed to install the drivers for
different phones. When a phone is connected and Windows asks for the drivers for this
device point it to this folder. If the drivers then fail to install it is likely that the
android_winusb.inf does not contain the IDs for the phone. In android_winusb.inf there
are two sections the one named [Google.NTx86] contains the details for 32 bit systems,
another named [Google.NTamd64] contains the details for 64 bit systems. The two
sections are in fact identical. For my phone to be recognised the following details needed
to be inserted in each of the two sections mentioned.
;Orange San Francisco
%SingleAdbInterface% = USB_Install, USB\VID_19D2&PID_1354&MI_00
%CompositeAdbInterface% = USB_Install, USB\VID_19D2&PID_1354&MI_02
These necessary details were obtained courtesy of a certain well known Internet search
engine. However, rather belatedly, Google now at last has a list of links to manufacturer
sites that provide USB drivers for their devices on this page Google USB Driver . The
actual link is OEM USB Drivers.
Note that for some devices, like my phone, Windows may try to install the drivers
without USB Debugging being enabled on the phone. This is likely to fail even when you
point Windows at the drivers that installed successfully with USB Debugging enabled.
As this will happen every time the phone is plugged in to USB the solution is to keep
USB Debugging enabled. This has no downside as, for this phone at least, the USB
access to the SD card is entirely independent of whether USB Debugging is enabled or
not.
More on adb and WiFi
Some devices seem to not have the adb daemon enabled for wireless. There is an
application called adbWireless that can overcome this for many, but not all, devices. It is
available on the Android Market, the latest version is 1.4.1, but it needs root access to the
phone to work.
Another application called UniversalAndroot may help here - it worked on my ZTE
30
31
32
Sub Globals
'These global variables will be redeclared each time the activity
is created.
'These variables can only be accessed from this module.
End Sub
Sub Activity_Create(FirstTime As Boolean)
End Sub
Sub Activity_Resume
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
Variables can be either global or local. Local variables are variables that are declared
inside a sub other than Process_Globals or Globals.
Local variables are local to the containing sub. Once the sub ends these variables no
longer exist.
Global variables can be accessed from all subs.
There are two types of global variables.
Process variables and activity variables.
Process variables - These variables live as long as the process lives.
You should declare these variables inside sub Process_Globals.
This sub is called once when the process starts (this is true for all activities, not just the
first activity).
These variables are the only "public" variables. Which means that they can be accessed
from other modules as well.
However, not all types of objects can be declared as process variables.
All of the views for example cannot be declared as process variables.
The reason is that we do not want to hold a reference to objects that should be destroyed
together with the activity.
In other words, once the activity is being destroyed, all of the views which are contained
in the activity are being destroyed as well.
If we hold a reference to a view, the garbage collector would not be able to free the
resource and we will have a memory leak.
The compiler enforces this requirement.
Activity variables - These variables are contained by the activity.
You should declare these variables inside Sub Globals.
33
These variables are "private" and can only be accessed from the current activity module.
All objects types can be declared as activity variables.
Every time the activity is created, Sub Globals is called (before Activity_Create).
These variables exist as long as the activity exists.
Sub Activity_Create (FirstTime As Boolean)
This sub is called when the activity is created.
The activity is created when the user first launches the application, the device
configuration has changed (user rotated the device) and the activity was destroyed, or
when the activity was in the background and the OS decided to destroy it in order to free
memory.
This sub should be used to load or create the layout (among other uses).
The FirstTime parameter tells us if this is the first time that this activity is created. First
time relates to the current process.
You can use FirstTime to run all kinds of initializations related to the process variables.
For example if you have a file with a list of values that you need to read, you can read it
if FirstTime is True and store the list as a process variable.
Now we know that this list will be available as long as the process lives and there is no
need to reload it even when the activity is recreated.
To summarize, you can test whether FirstTime is True and then initialize process
variables.
Sub Activity_Resume and Sub Activity_Pause (UserClosed As Boolean)
Each time the activity moves from the foreground to the background Activity_Pause is
called.
Activity_Pause is also called when the activity is in the foreground and a configuration
change occurs (which leads to the activity getting paused and then destroyed).
Activity_Pause is the last place to save important information.
Generally there are two types of mechanisms that allow you to save the activity state.
Information that is only relevant to the current application instance can be stored in one
or more process variables.
Other information should be stored in a persistent storage (file or database).
For example, if the user changed some settings you should save the changes to a
persistent storage at this point. Otherwise the changes may be lost.
Activity_Resume is called right after Activity_Create finishes or after resuming a paused
activity (activity moved to the background and now it returns to the foreground).
Note that when you open a different activity (by calling StartActivity), the current
activity is first paused and then the other activity will be created if needed and (always)
resumed.
As discussed above Activity_Pause is called every time that the activity moves from the
34
35
Now in order to catch the click event you should create a sub named Button_Click.
This sub should be located in the Activity module, as Code modules cannot catch events.
CallSub which internally uses the events mechanism cannot be used to call code module
subs (which can be called directly instead).
36
Service Modules
Basic4android v1.2 adds support for Service modules.
Service modules play an important role in the application and process life cycle.
Start with this tutorial if you haven't read it before: Android Process and activities life
cycle
Code written in an activity module is paused once the activity is not visible.
So by only using activities it is not possible to run any code while your application is not
visible.
Services life cycle is (almost) not affected by the current visible activity. This allows you
to run tasks in the background.
Services usually use the status bar notifications to interact with the user. Services do not
have any other visible elements. Services also cannot show any dialog (except of toast
messages).
Note that when an error occurs in a service code you will not see the "Do you want to
continue?" dialog. Android's regular "Process has crashed" message will appear instead.
Before delving into the details I would like to say that using services is simpler than it
may first sound. In fact for many tasks it is easier to work with a service instead of
an activity as a service is not paused and resumed all the time and services are not
recreated when the user rotates the screen. There is nothing special with code written in
service.
Code in a service module runs in the same process and the same thread as all other code.
It is important to understand how Android chooses which process to kill when it is low on
memory (a new process will later be created as needed).
A process can be in one of the three following states:
- Foreground - The user currently sees one of the process activities.
- Background - None of the activities of the process are visible, however there is a started
service.
- Paused - There are no visible activities and no started services.
Paused processes are the first to be killed when needed. If there is still not enough
memory, background processes will be killed.
Foreground processes will usually not be killed.
As you will soon see a service can also bring a process to the foreground.
Adding a service module is done by choosing Project - Add New Module - Service
Module.
The template for new services is:
Code:
37
Sub Process_Globals
End Sub
Sub Service_Create
End Sub
Sub Service_Start
End Sub
Sub Service_Destroy
End Sub
Sub Process_Globals is the place to declare the service global variables. There is no
other Globals sub like in Activity as Service doesn't support Activity objects.
Sub process globals should only be used to declare variables. It should not run any other
code as it might fail. This is true for other modules as well.
Note that Process_Global variables are kept as long as the process runs and are accessible
from other modules.
Sub Service_Create is called when the service is first started. This is the place to
initialize and set the process global variables. Once a service is started it stays alive until
you call StopService or until the whole process is destroyed.
Sub Service_Start is called each time you call StartService (or StartServiceAt). When
this subs runs the process is moved to the foreground state. Which means that the OS will
not kill your process until this sub finishes running. If you want to run some code every
couple of minutes / hours you should schedule the next task with StartServiceAt inside
this sub.
Sub Service_Destroy is called when you call StopService. The service will not be
running after this sub until you call StartService again (which will run Sub
Service_Create followed by Sub Service_Start).
A good design is to make the activity fetch the required data from the service in Sub
Activity_Resume. The activity can fetch data stored in a process global variable or it can
call a service Sub with CallSub method.
- Running a long operation. For example downloading a large file from the internet. In
this case you can call Service.StartForeground (from the service module). This will move
your activity to the foreground state and will make sure that the OS doesn't kill it. Make
sure to eventually call Service.StopForeground.
- Scheduling a repeating task. By calling StartServiceAt you can schedule your service to
run at a specific time. You can call StartServiceAt in Sub Service_Start to schedule the
next time and create a repeating task (for example a task that checks for updates every
couple of minutes).
- Run a service after boot. By checking Project - Service properties - Run At Boot your
service will run after boot is completed.
Notifications
Status bar notifications can be displayed by activities and services.
Usually services use notifications to interact with the user. The notification displays an
icon in the status bar. When the user pulls the status bar they see the notification message.
Example of a notification (using the default icon):
The user can press on the message, which will open an activity as configured by the
Notification object.
The notification icon is an image file which you should manually put in the following
folder: <project folder>\Object\res\drawable.
Accessing other modules
39
Process global objects are public and can be accessed from other modules.
Using CallSub method you can also call a sub in a different module.
It is however limited to non-paused modules. This means that one activity can never
access a sub of a different activity as there could only be one running activity.
However an activity can access a running service and a service can access a running
activity.
Note that if the target component is paused then an empty string returns.
No exception is thrown.
You can use IsPause to check if the target module is paused.
For example if a service has downloaded some new information it can call:
Code:
CallSub(Main, "RefreshData")
If the Main activity is running it will fetch the data from the service process global
variables and will update the display.
It is also possible to pass the new information to the activity sub. However it is better to
keep the information as a process global variable. This allows the activity to call
RefreshData whenever it want and fetch the information (as the activity might be paused
when the new information arrived).
Note that it is not possible to use CallSub to access subs of a Code module.
Examples:
Downloading a file using a service module
Periodically checking Twitter feeds
40
All other types, including arrays of primitives types and strings are categorized as nonprimitive types.
When you pass a non-primitive to a sub or when you assign it to a different variable, a
copy of the reference is passed.
This means that the data itself isn't duplicated.
It is slightly different than passing by reference as you cannot change the reference of the
original variable.
All types can be treated as Objects.
Collections like lists and maps work with Objects and therefore can store any value.
Here is an example of a common mistake, where the developer tries to add several arrays
to a list:
Code:
Dim arr(3) As Int
Dim List1 As List
List1.Initialize
For i = 1 To 5
arr(0) = i * 2
arr(1) = i * 2
arr(2) = i * 2
41
42
End Sub
Sub Btn_Click
Dim b As Button
b = Sender 'Cast the Object to Button
b.Color = Colors.RGB(Rnd(0, 255), Rnd(0, 255), Rnd(0, 255))
End Sub
Scope
Variables that are declared in Sub Globals or Sub Process_Globals are global and can be
accessed from all subs.
Other variables are local and can only be accessed from the sub that they are declared in.
See the Activity lifecycle tutorial for more information about Globals vs.
Process_Globals variables.
Tips
All views types can be treated as Views. This allows you to change the common
properties of views easily.
For example, the following code disables all views that are direct children of the activity:
Code:
For i = 0 To Activity.NumberOfViews - 1
Dim v As View
v = Activity.GetView(i)
v.Enabled = False
Next
Code:
For i = 0 To Activity.NumberOfViews - 1
Dim v As View
v = Activity.GetView(i)
If v Is Button Then 'check whether it is a Button
v.Enabled = False
End If
Next
The Type keyword allows you to create your own type of objects. Custom types behave
exactly like other non-primitive types.
44
45
In our case we have two file. CountryCodes.txt is a text file containing the list of
currencies. Each line contains exactly one value.
layout1.bal is the layout file created with the designer. Layout files are added
automatically to the file manager.
Note that the layout file contains another two image files, the buttons arrows. These files
are listed in the designer. If we remove layout1.bal they will be removed from the
package as well.
The packaged files are also named assets. Locally they are stored under the Files sub
folder.
This code reads the text file and stores the data in a list:
Code:
If FirstTime Then
countries = File.ReadList(File.DirAssets, "CountryCodes.txt")
File.ReadList is a convenient method that opens a file and adds all its lines to a List. Files
are always referenced by their folder and name.
The assets are referenced by the File.DirAssets value.
Android file system is case sensitive. Which means that image1.jpg is not the same
as Image1.jpg (unlike Windows file system).
Structures
You can create new types or structures using the Type keyword. Later you can declare
variables of these new types.
Types can hold any other objects, including other types and including themselves (and
including arrays of all of these).
Structures will be covered more deeply in a different tutorial...
Structures are declared in one of the global subs.
Code:
Type MyTag (FromValue As EditText, ToValue As EditText, _
FromCurrency As Spinner, ToCurrency As Spinner)
Dim CurrentTask As MyTag
This code declares a type that holds two EditTexts (textboxes) and two Spinners
(Comboboxes).
We also declare a variable of that type named CurrentTask.
In the code you will see that we have another type named StateType which we use to
store the current state.
All views have a property named Tag. You can set this property to any object you like.
We are using it together with the Sender keyword to handle both buttons with the same
sub.
46
Libraries
As you can see in the image, the Libraries tab page shows a list of available libraries. The
checked libraries are referenced. Note that you cannot remove the reference to the core
library.
Adding additional libraries
Libraries are made of a pair of files. The xml file that describes the library and the jar file
which holds the compiled code.
Additional libraries and updates to official libraries are available here:
http://www.basic4ppc.com/forum/addit...icial-updates/
Note that only users who bought Basic4android can download these libraries.
To add a library to Basic4android all you need to do is to copy both files to a folder
recognized by Basic4android.
By default this is the 'Libraries' folder that is usually located in: c:\Program
Files\Anywhere Software\Basic4android\Libraries.
You can also configure an additional libraries folder by choosing Tools - Configure
Paths. Note that the additional folder is scanned first for libraries. This means that you
can update an existing library by putting the new files in the additional libraries folder
(there is no need to delete the old files from the internal folder).
Http library
The Http library includes three objects.
HttpClient - This is the main object that executes and manages the requests and
responses. The HttpClient can execute multiple requests concurrently.
It is very important to declare it as a Process global. The HttpClient handles requests that
run in the background and it should not be tied to the activity life cycle.
Communication is done in two steps. First a connection is established by sending a
HttpRequest object and then the response is read from the server.
The first step is always a non blocking action. It can take a long period till the connection
47
is established and you do not want to make your application be non-responsive at this
time. Note that Android has a special "Application not responding" dialog which allows
the user to force close the application.
The second step, which is the consumption of the response can be either blocking or
nonblocking. If you download a file for example you should probably choose the
nonblocking option.
This code creates and sends the GET request.
Code:
Dim request As HttpRequest
request.InitializeGet(URL & fromCountry & "&ToCurrency=" &
toCountry)
request.Timeout = 10000 'set timeout to 10 seconds
If HttpClient1.Execute(request, 1) = False Then Return 'Will be
false if their is already a running task (with the same id).
We are setting the timeout to 10 seconds which is quite short. The default is 30 seconds.
The target web service is pretty unstable, which makes things more interesting. I prefer it
to fail fast in our case.
HttpClient.Execute method receives two parameters. The first is the request object and
the second is the Task ID. This integer will be passed back in the ResponseSuccess or
ResponseError events.
It allows you to differentiate between different tasks that may be running in the
background.
HttpClient.Execute will return false if their is already a running task with the same ID.
This helps you prevent unnecessary multiple requests.
You can also check the status of running tasks with the IsBackgroundTaskRunning
keyword.
Once the response is ready, ResponseSuccess or ResponseError will be raised. If things
went well, we can now read the response, find the rate and display it. Otherwise we
display a "toast" message with the error message.
As I wrote above, this specific web service seems to be unstable so your experience may
vary.
State
As discussed in the life cycle tutorial we are required to save and restore the state of the
application. In our case the state is made of the values in the text boxes and the current
selected currencies.
48
On the first run we set its values with the default values we want:
Code:
Sub ResetState
'set the starting state
State.TextUp = 1
State.TextDown = ""
State.IndexUp = 0 'USD
State.IndexDown = 43 'Euro
End Sub
In Activity_Resume we read the values and set the required views. Note that
Activity_Resume is called right after Activity_Create. So it will also be called on the first
time we run the application.
In Activity_Pause we save the value in the state object (which is a process variable).
Note that if the user pressed on the back key (which means that he wants to close our
application) we return the state to the initial state. Therefore the user will see a "clean
new" application the next time he will run our application.
49
50
51
toCountry = CurrentTask.ToCurrency.SelectedItem.SubString2(0, 3)
Dim request As HttpRequest
request.InitializeGet(URL & fromCountry & "&ToCurrency=" &
toCountry)
request.Timeout = 10000 'set timeout to 10 seconds
If HttpClient1.Execute(request, 1) = False Then Return 'Will be
false if their is already a running task (with the same id).
ProgressDialogShow("Calling server...")
End Sub
Sub HttpClient1_ResponseSuccess (Response As HttpResponse, TaskId As
Int)
Log("ResponseSuccess")
ProgressDialogHide
Dim result As String
result = Response.GetString("UTF8") 'Convert the response to a
string
Log(result)
Dim rate As Double
'Parse the result
i = result.IndexOf(".NET/")
If i = -1 Then
Msgbox("Invalid response.", "Error")
Return
End If
i2 = result.IndexOf2("<", i + 1)
rate = result.substring2(i + 7, i2)
Log("Rate = " & rate)
If IsNumber(CurrentTask.FromValue.Text) = False Then
Msgbox("Please enter a valid number.", "Error")
Return
End If
'Set the answer
CurrentTask.ToValue.Text = Round2(CurrentTask.FromValue.Text *
rate, 2)
End Sub
Sub HttpClient1_ResponseError (Reason As String, StatusCode As Int,
TaskId As Int)
Log(Reason)
Log(StatusCode)
ProgressDialogHide
msg = "Error connecting to server."
If reason <> Null Then msg = msg & CRLF & Reason
ToastMessageShow (msg, True)
End Sub
52
53
TextReader and TextWriter have an advantage over the File read/write methods when
working with large files. The File methods read the file completely and store its content
in memory. In many cases this is the most convenient solution, however if you work with
large files (more than 1-2mb) you may prefer to work with TextReader or TextWriter.
File.WriteString - Writes the given text to a new file.
File.ReadString - Reads a file and returns it content as a string.
File.WriteList - Writes all values stored in a list to a file. All values are converted to
string type if required. Each value will be stored in its own line.
Note that if a value contains the new line character it will saved over more than one line
and when you read it, it will be read as multiple items.
File.ReadList - Reads a file and stores each line as an item in a list.
File.WriteMap - Takes a map object which holds pairs of key and value elements and
stores it in a text file. The file format is known as Java Properties file: .properties Wikipedia, the free encyclopedia
The file format is not too important unless the file is supposed to be edited manually.
This format makes it easy to edit it manually.
One common usage of File.WriteMap is to save a map of "settings" to a file.
File.ReadMap - Reads a properties file and returns its key/value pairs as a Map object.
Note that the order of entries returned might be different than the original order.
Example:
Code:
Sub Process_Globals
End Sub
Sub Globals
End Sub
Sub Activity_Create(FirstTime As Boolean)
If File.ExternalWritable = False Then
Msgbox("Cannot write on storage card.", "")
Return
End If
SaveStringExample
ReadStringExample
WriteListExample
ReadListExample
WriteMapExample
ReadMapExample
WriteTextWriter
54
ReadTextReader
End Sub
Sub SaveStringExample
File.WriteString(File.DirRootExternal, "String.txt", _
"This is some string" & CRLF & "and this is another one.")
End Sub
Sub ReadStringExample
Msgbox(File.ReadString(File.DirRootExternal, "String.txt"), "")
End Sub
Sub WriteListExample
Dim List1 As List
List1.Initialize
For i = 1 To 100
List1.Add(i)
Next
File.WriteList(File.DirRootExternal, "List.txt", List1)
End Sub
Sub ReadListExample
Dim List1 As List
'We are not initializing the list because it just holds the list
that returns from File.ReadList
List1 = File.ReadList(File.DirRootExternal, "List.txt")
Msgbox("List1.Size = " & List1.Size & CRLF & "The third item is: "
& List1.Get(2), "")
End Sub
Sub WriteMapExample
Dim Map1 As Map
Map1.Initialize
For i = 1 To 10
Map1.Put("Key" & i, "Value" & i)
Next
File.WriteMap(File.DirRootExternal, "Map.txt", Map1)
End Sub
Sub ReadMapExample
Dim Map1 As Map
'Again we are not initializing the map.
Map1 = File.ReadMap(File.DirRootExternal, "Map.txt")
'Append all entries to a string builder
Dim sb As StringBuilder
sb.Initialize
sb.Append("The map entries are:").Append(CRLF)
For i = 0 To Map1.Size - 1
sb.Append("Key = ").Append(Map1.GetKeyAt(i)).Append(", Value =
")
sb.Append(Map1.GetValueAt(i)).Append(CRLF)
55
Next
Msgbox(sb.ToString,"")
End Sub
Sub WriteTextWriter
Dim TextWriter1 As TextWriter
TextWriter1.Initialize(File.OpenOutput(File.DirRootExternal,
"Text.txt", False))
For i = 1 To 10
TextWriter1.WriteLine("Line" & i)
Next
TextWriter1.Close
End Sub
Sub ReadTextReader
Dim TextReader1 As TextReader
TextReader1.Initialize(File.OpenInput(File.DirRootExternal,
"Text.txt"))
Dim line As String
line = TextReader1.ReadLine
Do While line <> Null
Log(line) 'write the line to LogCat
line = TextReader1.ReadLine
Loop
TextReader1.Close
End Sub
56
Views
Button:
Checkbox:
EditText (TextBox):
Can also be multiline.
ImageView:
Label:
57
ListView (ListBox):
Panel:
Can hold other views (and load layout files).
ProgressBar:
RadioButton:
58
ScrollView:
SeekBar (TrackBar):
Spinner (ComboBox):
TabHost (TabControl):
ToggleButton:
59
WebView:
Dialogs
Basic4android dialogs are blocking modal dialogs. This means that the code execution stops while the
dialog is shown.
Some of the dialogs are shown by calling a keyword (like Msgbox). Other dialogs are included in Andrew's
excellent dialogs library.
Internal dialogs
InputList:
InputMultiList:
60
Dialogs library
ColorDialog:
61
ColorPickerDialog:
DateDialog:
InputDialog:
NumberDialog:
62
TimeDialog:
FileDialog:
63
GPS tutorial
The GPS is an important feature of many Android devices.
Fortunately it is pretty easy to work with it.
In this tutorial we will cover a simple program that shows the current position as well as
the satellites status.
64
There are three types of relevant objects. The main one is GPS.
The GPS manages the connection and events. The second is Location.
A Location is a structure that holds the data available regarding a specific "fix". The data
includes the latitude and longitude coordinates, the time (expressed as ticks) of this fix
and other information like bearing, altitude and so on.
It may happen that not all information is available (due to poor reception for example).
The Location also includes other functionalities like calculating the distance and bearing
to another location and methods to convert the coordinates string formats.
Usually you will work with Location objects passed to you in the LocationChanged
events. However you can also initialize such objects yourself (this is useful for
calculating distance and bearing between locations).
The last one is GPSSatellite. This is a structure that holds various information regarding
the currently known satellites. It is passed to you in GPSStatus event.
Back to GPS.
The GPS object should be declared as a Process_Global object. Otherwise new instances
will be created each time the activity is recreated.
The first step is to initialize the object. Like many other Initialize methods this one
expects an EventName parameter. This is the prefix for the events that will be raised by
the GPS object.
Here is the complete code:
Code:
Sub Process_Globals
Dim GPS1 As GPS
End Sub
Sub Globals
Dim lblLon As Label
Dim lblLat As Label
Dim lblSpeed As Label
Dim lblSatellites As Label
End Sub
Sub Activity_Create(FirstTime As Boolean)
If FirstTime Then
GPS1.Initialize("GPS")
End If
Activity.LoadLayout("1")
End Sub
Sub Activity_Resume
If GPS1.GPSEnabled = False Then
65
The next step is to tell the GPS to start listening for data. The GPS can consume quite a
lot of battery. Therefore it is recommended to stop using the GPS whenever it is not
necessary. It is recommended to start listening in Activity_Resume and stop listening in
Activity_Pause.
It may happen that the user has turned off the GPS. Due to privacy concerns the Android
OS doesn't allow you to turn the GPS on programatically. The best thing that you can do
is to ask the user to enable the GPS device.
The following code shows a message if the GPS is not enabled and also opens the GPS
control panel so the user only needs to check the GPS option:
Code:
66
Sub Activity_Resume
If GPS1.GPSEnabled = False Then
ToastMessageShow("Please enable the GPS device.", True)
StartActivity(GPS1.LocationSettingsIntent) 'Will open the
relevant settings screen.
Else
GPS1.Start(0, 0) 'Listen to GPS with no filters.
End If
End Sub
If the GPS is enabled we are starting to listen for data. The Start method receives two
values which are the minimum time (milliseconds) and the minimum distance (meters)
between events. An event will be raised when at least one of these criterions is met. This
can help saving battery.
In our case we are passing 0 and therefore will get all fix events.
The GPS raises three events:
- GPS_LocationChanged (Location1 As Location)
This is the main event. Location1 holds the data for the new fix.
-GPS_GpsStatus (Satellites As List)
This event allows you to display information about the currently available satellites. Note
that not all satellites in the list are actually used for calculating the last fix. So it is
possible that the list will include several satellites but still the reception is not good
enough for a fix.
- GPS_UserEnabled (Enabled As Boolean)
This event is raised whenever the user changes the status of the GPS device. It is also
raised right after calling Start.
The program is attached.
67
In this case the file was added with the file manager and therefore File.DirAssets is used.
Now we can start playing the file with:
Code:
MediaPlayer1.Play
68
69
70
ListView tutorial
The ListView control is a very powerful control. It allows you to show short or long lists
in a very "sleek" way.
Creating a simple list is easy:
Code:
Sub Globals
Dim ListView1 As ListView
End Sub
Sub Activity_Create(FirstTime As Boolean)
ListView1.Initialize("ListView1")
For i = 1 To 300
ListView1.AddSingleLine("Item #" & i)
Next
Activity.AddView(ListView1, 0, 0, 100%x, 100%y)
End Sub
Sub ListView1_ItemClick (Position As Int, Value As Object)
Activity.Title = Value
End Sub
The ListView can be added programmatically or with the designer. For now the items
must be added with code.
About the code:
- ListView1.Initialize("ListView1") - Here we initialize the list and set the event name
property to ListView1. Which means that in order to catch related events we should have
subs like: ListView1_ItemClick.
71
We can set different bitmaps to different items. Note that this code loads an image file
named button.gif. This file should be added to the Files tab (in the right pane). You
can download the project which is attached to this post.
Customizing each type
Each of the three types can be customized. The change will affect all items of that type.
The ListView has three "models" which are stored under:
- SingleLineLayout
- TwoLinesLayout
- TwoLinesAndBitmap
72
Each of this model has an ItemHeight property, a Background property and one or more
views properties. Again, if you change any of these properties it will affect all the items
of this type.
Example of customizing the single line items:
Code:
ListView1.SingleLineLayout.ItemHeight = 100dip
ListView1.SingleLineLayout.Label.TextSize = 20
ListView1.SingleLineLayout.Label.TextColor = Colors.Blue
ListView1.SingleLineLayout.Label.Gravity = Gravity.CENTER
For i = 1 To 300
ListView1.AddSingleLine("Item #" & i)
ListView1.AddTwoLines("Item #" & i, "This is the second line.")
ListView1.AddTwoLinesAndBitmap("Item #" & i, "This is the
second line.", Bitmap1)
Next
Result:
Note that the ItemHeight is set to 100dip. The 'dip' unit causes it to automatically scale
the height based on the current device scale. For the TextSize it is a mistake to use dip
units as the text size is already measured in scaled units.
The above code is equivalent to this code (which is a bit more clear):
Code:
ListView1.SingleLineLayout.ItemHeight = 100dip
Dim label1 As Label
label1 = ListView1.SingleLineLayout.Label 'set the label to the
model label.
label1.TextSize = 20
73
label1.TextColor = Colors.Blue
label1.Gravity = Gravity.CENTER
In a similar way you can change the way the other types look.
The other types have additional views: SecondLabel and ImageView.
Return value
First notice that there is no selected item. The reason is that the combination of scrolling
the list with finger and scrolling with the wheel or keyboard makes it non relevant.
You should catch the ItemClick event and then handle the clicked item.
The value of the clicked item is passed as a parameter.
Now, what is a value of an item???
By default this is the text stored in the first line.
However you can change it to any object you like by using:
AddSingleLine2, AddTwoLines2 and AddTwoLinesAndBitmap2 methods. These
methods receive an additional parameter which is the return value. This allows you to
pass more information as required by your application.
Background optimization
There is a hidden assumption that the background behind the ListView is solid black. If
you set the background to something else like a gradient background or image you will
see that during scrolling the background disappears.
You can change the background scrolling color with the ScrollingBackgroundColor
property. If the background is not solid color set it to Colors.Transparent.
Example (the activity background is a gradient):
Code:
Dim GD As GradientDrawable
GD.Initialize("TR_BL", Array As Int(Colors.Gray, Colors.LightGray))
Activity.Background = GD
ListView1.ScrollingBackgroundColor = Colors.Transparent
Tips
If you want a single line item with a bitmap (and do not need two lines and a bitmap),
you can set the visible property of the second label to false.
If you have many items then you should enable the fast scroller:
Code:
ListView1.FastScrollEnabled = true
74
75
ScrollView tutorial
The ScrollView is a very useful container which allows you to show many other views on
a small screen.
The ScrollView holds an inner panel view which actually contains the other views.
The user vertically scrolls the inner panel as needed.
In this example we will load images from the sdcard and add those to a ScrollView.
76
The code that is loading the bitmaps looks for jpg files under /sdcard/Images
If you want to run this program on the emulator you will first need to create this folder
and copy some images to it.
This is done with the "adb" command, that comes with Android SDK.
Open a shell console (Windows Start - Run - Cmd).
Go to the sdk tools folder and issue:
Code:
c:\android-sdk-windows\tools> adb -e shell mkdir /sdcard/Images
The -e parameter tells adb to send the command to the only connected emulator.
The command is mkdir with the name of the folder.
Note that Android file system is case sensitive.
Now we need to copy some images to this folder.
This is done with the push command:
Code:
adb -e push "C:\temp" /sdcard/Images
This will copy all files under c:\temp to the Images folder.
The emulator is very slow compared to a real device. While on a real device 50 large
images load in 2-3 seconds. It can take a long time for the emulator to load a few small
images. I recommend you to copy 2 - 3 small images at most.
Also the experience of the ScrollView on the emulator cannot be compared to a real
device (with capacitive screen).
The program is attached.
77
TabHost tutorial
The TabHost view is a very important view. It allows you to add several layouts into the
same activity.
The designer currently doesn't support adding views directly to the TabHost.
You can only add the TabHost and set its layout:
There are several ways to add tab pages. Usually it is recommended to create a layout file
78
AddTabWithIcon receives two bitmaps. There are actually two icons. One when the tab
is selected and one when the tab is not selected. The guidelines recommend creating a
dark version for the selected icon and a light version for the not selected icon.
You can manually change the selected tab by setting the CurrentTab property.
The example is attached.
79
This application gets this html page: Flickr: Explore interesting photos from the last 7
days in FlickrLand... , parses it and finds the 9 image links.
It then sends a request for each of the images. Downloaded images are then displayed in
the ImageViews.
Each time you press on the Connect button 9 new images appear.
Pressing on an image shows it larger in a second activity (note that the full image is not
downloaded so the second activity just shows the thumbnail with its original size).
If you run this program you can see that all requests are handled in the background and
that images appear whenever they are ready.
The TaskId plays an important role here. Each image request is sent with a TaskId
between 0 to 8. This TaskId is later used to get the correct ImageView from the
ImageViews array.
80
It is recommended that you first read the Activities life cycle tutorial if you haven't read it
before.
In order to add a new or existing activity to your project you should choose Project - Add
New / Existing Module.
Variables declared in Sub Process_Globals are public variables and can be accessed from
other activities. Therefore we will save the chosen value in such a variable.
When the user presses on the "choose item" button we open the second activity:
Code:
Sub Button1_Click
StartActivity(Activity2)
End Sub
When we open an activity the current one is first paused and then the target activity is
resumed (and created if needed).
You can see it in the logs:
81
Sub Process_Globals
Dim result As String
result = ""
End Sub
Sub Globals
Dim ListView1 As ListView
End Sub
Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("2")
For i = 1 To 100
ListView1.AddSingleLine("Item #" & i)
Next
End Sub
Sub Activity_Resume
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
Sub ListView1_ItemClick (Position As Int, Value As Object)
result = value 'store the value in the process global object.
StartActivity(Main) 'show the main activity again
End Sub
When the user presses on an item we store the value in the 'result' variable and we return
to the main activity.
The main activity Resume event will be raised. So in this event we check if 'result'
variable is not empty and change the label's text.
Code:
Sub Activity_Resume
If Activity2.result.Length > 0 Then
Label1.Text = "You have chosen: " & Activity2.result
Else
Label1.Text = "Please press on the button and choose an item."
End If
End Sub
In more complex applications with more than two activities you can use a process global
variable in the main activity. Before starting an activity you can set this variable to some
constant and then in Sub Activity_Resume check the value of this variable to know which
activity was started and act accordingly.
The project is attached.
82
The interesting thing about this declaration is that we are using the current type as a field.
The ability to declare such recursive types is very powerful.
Before we can access any of the type fields, it should be initialized by calling its Initialize
method. Note that if the fields were initialized automatically then it would not have been
possible to create such recursive types.
Note that if your type only includes numeric fields and strings then there is no need to
call Initialize (though nothing bad will happen if you do call it).
Once we declared a type we can use it like any other "regular" types. It can be passed to
subs, you can create an array of it and so on.
Lets build our list:
Code:
Sub InitializeList (Value As Int)
Head.Initialize
Head.Val = Value
Last = Head 'The last item is currently the head.
End Sub
The InitializeList sub initializes the head element, sets its value and sets the Last variable
to reference the head element (as this is the only item in the list).
Adding an item:
Code:
Sub AddElement(Value As Int)
83
We are creating a new element. The current last element NextElement field is set to the
new element and eventually the last variable is updated to the new element.
Going over all the items in the list is done by starting with the head element and then
going forward by calling NextElement. When we reach an uninitialized element we stop.
Code:
Sub ListToString As String
Dim e As Element
Dim sb As StringBuilder
sb.Initialize
e = Head
Do While e.IsInitialized = True
sb.Append(e.Val).Append(CRLF)
e = e.NextElement
Loop
Return sb.ToString
End Sub
84
SQL tutorial
This tutorial covers the SQL library and its usage with Basic4android.
There are many general SQL tutorials that cover the actual SQL language. If you are not
familiar with SQL it is recommended to start with such a tutorial.
SQL Introduction
Android uses SQLite which is an open source SQL implementation.
Each implementation has some nuances. The following two links cover important
information regarding SQLite.
SQLite syntax: Query Language Understood by SQLite
SQLite data types: Datatypes In SQLite Version 3
SQL in Basic4android
The first step is to add a reference to the SQL library. This is done by going to the
Libraries tab and checking SQL.
There are two types in this library.
An SQL object gives you access to the database.
The Cursor object allows you to process queries results.
Usually you will want to declare the SQL object as a process global object. This way it
will be kept alive when the activity is recreated.
SQLite stores the database in a single file.
When we initialize the SQL object we pass the path to a database file (which can be
created if needed).
Code:
Sub Process_Globals
Dim SQL1 As SQL
End Sub
Sub Globals
End Sub
Sub Activity_Create(FirstTime As Boolean)
If FirstTime Then
SQL1.Initialize(File.DirDefaultExternal, "test1.db", True)
End If
CreateTables
FillSimpleData
LogTable1
InsertManyRows
Log("Number of rows = " & SQL1.ExecQuerySingleResult("SELECT
count(*) FROM table1"))
85
The SQL1 object will only be initialized once when the process starts.
In our case we are creating it in the sd card. The last parameter (CreateIfNecessary) is
True so the file will be created if it doesn't exist.
There are three types of methods that execute SQL statements.
ExecNonQuery - Executes a "writing" statement and doesn't return any result. This can
be for example: INSERT, UPDATE or CREATE TABLE.
ExecQuery - Executes a query statement and returns a Cursor object that is used to
process the results.
ExecQuerySingleResult - Executes a query statement and returns the value of the first
column in the first row in the result set. This method is a shorthand for using ExecQuery
and reading the value with a Cursor.
We will analyze the example code:
Code:
Sub CreateTables
SQL1.ExecNonQuery("DROP TABLE IF EXISTS table1")
SQL1.ExecNonQuery("DROP TABLE IF EXISTS table2")
SQL1.ExecNonQuery("CREATE TABLE table1 (col1 TEXT , col2 INTEGER,
col3 INTEGER)")
SQL1.ExecNonQuery("CREATE TABLE table2 (name TEXT, image BLOB)")
End Sub
The above code first deletes the two tables if they exist and then creates them again.
Code:
Sub FillSimpleData
SQL1.ExecNonQuery("INSERT INTO table1 VALUES('abc', 1, 2)")
SQL1.ExecNonQuery2("INSERT INTO table1 VALUES(?, ?, ?)", Array As
Object("def", 3, 4))
End Sub
In this code we are adding two rows. SQL.ExecNonQuery2 receives two parameters. The
first parameter is the statement which includes question marks. The question marks are
then replaced with values from the second List parameter. The List can hold numbers,
strings or arrays of bytes (blobs).
Arrays are implicitly converted to lists so instead of creating a list we are using the Array
keyword to create an array of objects.
Code:
86
Sub LogTable1
Dim Cursor1 As Cursor
Cursor1 = SQL1.ExecQuery("SELECT col1, col2, col3 FROM table1")
For i = 0 To Cursor1.RowCount - 1
Cursor1.Position = i
Log("************************")
Log(Cursor1.GetString("col1"))
Log(Cursor1.GetInt("col2"))
Log(Cursor1.GetInt("col3"))
Next
Cursor1.Close
End Sub
This code uses a Cursor to log the two rows that were previously added.
SQL.ExecQuery returns a Cursor object.
Then we are using the For loop to iterate over all the results.
Note that before reading values from the Cursor we are first setting its position (the
current row).
Code:
Sub InsertManyRows
SQL1.BeginTransaction
Try
For i = 1 To 500
SQL1.ExecNonQuery2("INSERT INTO table1 VALUES ('def', ?,
?)", Array As Object(i, i))
Next
SQL1.TransactionSuccessful
Catch
Log(LastException.Message)
End Try
SQL1.EndTransaction
End Sub
This code is an example of adding many rows. Internally a lock is acquired each time a
"writing" operation is done.
By explicitly creating a transaction the lock is acquired once.
The above code took less than half a second to run on a real device.
Without the BeginTransaction / EndTransaction block it took about 70 seconds.
A transaction block can also be used to guarantee that a set of changes were successfully
done. Either all changes are made or none are made.
By calling SQL.TransactionSuccessful we are marking this transaction as a successful
transaction. If you omit this line, all the 500 INSERTS will be ignored.
It is very important to call EndTransaction eventually.
Therefore the transaction block should usually look like:
Code:
SQL1.BeginTransaction
87
Try
'Execute the sql statements.
SQL.TransactionSuccessful
Catch
'the transaction will be cancelled
End Try
SQL.EndTransaction
Note that using transactions is only relevant when doing "writing" operations.
Blobs
The last two methods write an image file to the database and then read it and set it as the
activity background.
Code:
Sub InsertBlob
'convert the image file to a bytes array
Dim InputStream1 As InputStream
InputStream1 = File.OpenInput(File.DirAssets, "smiley.gif")
Dim OutputStream1 As OutputStream
OutputStream1.InitializeToBytesArray(1000)
File.Copy2(InputStream1, OutputStream1)
Dim Buffer() As Byte 'declares an empty array
Buffer = OutputStream1.ToBytesArray
'write the image to the database
SQL1.ExecNonQuery2("INSERT INTO table2 VALUES('smiley', ?)", Array
As Object(Buffer))
End Sub
Here we are using a special type of OutputStream which writes to a dynamic bytes array.
File.Copy2 copies all available data from the input stream into the output stream.
Then the bytes array is written to the database.
Code:
Sub ReadBlob
Dim Cursor1 As Cursor
'Using ExecQuery2 is safer as it escapes special characters
automatically.
'In this case it doesn't really matter.
Cursor1 = SQL1.ExecQuery2("SELECT image FROM table2 WHERE name =
?", Array As String("smiley"))
Cursor1.Position = 0
Dim Buffer() As Byte 'declare an empty byte array
Buffer = Cursor1.GetBlob("image")
Dim InputStream1 As InputStream
InputStream1.InitializeFromBytesArray(Buffer, 0, Buffer.Length)
Dim Bitmap1 As Bitmap
Bitmap1.Initialize2(InputStream1)
88
InputStream1.Close
Activity.SetBackgroundImage(Bitmap1)
End Sub
89
The StartElement is raised when an element begins. This event includes the element's
attributes list.
EndElement is raised when an element ends. This event includes the element's text.
In this example we will parse the forum RSS feed. RSS is formatted using XML.
A simplified example of this RSS is:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rss version="2.0">
<channel>
<title>Basic4ppc / Basic4android - Android programming</title>
<link>http://www.basic4ppc.com/forum</link>
<description>Basic4android - android programming and
development</description>
<ttl>60</ttl>
<image>
<url>http://www.basic4ppc.com/forum/images/misc/rss.jpg</url>
<title>Basic4ppc / Basic4android - Android
programming</title>
<link>http://www.basic4ppc.com/forum</link>
</image>
<item>
<title>Phone library was updated - V1.10</title>
<link>http://www.basic4ppc.com/forum/additional-librariesofficial-updates/6859-phone-library-updated-v1-10-a.html</link>
<pubDate>Sun, 12 Dec 2010 09:27:38 GMT</pubDate>
<guid
isPermaLink="true">http://www.basic4ppc.com/forum/additional-librariesofficial-updates/6859-phone-library-updated-v1-10-a.html</guid>
</item>
...MORE ITEMS HERE
90
</channel>
</rss>
For each item we are interested in two values. The title and the link.
The SaxParser object includes a handy list that holds the names of all the current parents
elements.
This is useful as it will help us find the "correct" 'title' and 'link' elements. The correct
elements are the ones under the 'item' element.
The parsing code in this case is pretty simple:
Code:
Sub Parser_StartElement (Uri As String, Name As String, Attributes As
Attributes)
End Sub
91
92
93
You can see in the attached code that the "take picture" button is only enabled when the
camera is ready.
For now the camera orientation is always set to landscape mode. Usually you will want to
force the activity to also be in landscape mode. This is done by checking 'Lansdcape' in
Project - Orientations Supported menu.
On the emulator the preview images will show a moving check board.
94
95
End Sub
Here we are checking if the Bluetooth device is enabled. If it is not enabled we ask the
user to enable it. Note that by putting this code in Activity_Resume (and not
Activity_Create) this test will happen every time the activity is resumed. So if the user
goes to the settings screen, enables the device and then returns to our application we will
now know that the Bluetooth is enabled.
If the Bluetooth is enabled we start listening for incoming connections. This allows other
devices to connect with our device. This is not required if you connect to a device that
listens for connections (like external GPS for example).
Note that calling Listen more than once doesn't do anything. So we are safe calling it this
way.
When the user presses on the Connect menu item we show the user the list of known
paired devices. When the user clicks on a device name we fetch its MAC address from
the map and connect to it:
96
Code:
Sub mnuConnect_Click
Dim PairedDevices As Map
PairedDevices = Serial1.GetPairedDevices
Dim l As List
l.Initialize
For i = 0 To PairedDevices.Size - 1
l.Add(PairedDevices.GetKeyAt(i)) 'add the friendly name to the
list
Next
Dim res As Int
res = InputList(l, "Choose device", -1) 'show list with paired
devices
If res <> DialogResponse.CANCEL Then
Serial1.Connect(PairedDevices.Get(l.Get(res))) 'convert the
name to mac address
End If
End Sub
The connection is not established immediately. It will happen in the background. When
the connection is established, the Connected event will be raised. A 'Success' parameter
tells us if the connection is successful.
The Connected event can also be raised if an incoming connection is established.
Code:
97
If the connection was established successfully we can start the data transfer.
TextReader1 and TextWriter1 are process global object. We now initialize them using the
serial stream. This will allow us to send and receive text over our newly created
connection.
Timer1 is used to test whether there is incoming data (and read this data). Now we enable
it and start listening.
If the connection is not successful we retrieve the exception and show it.
Sending messages - When the user presses on btnSend we send the text:
Code:
Sub btnSend_Click
If connected Then
TextWriter1.WriteLine(txtSend.Text)
TextWriter1.Flush
txtSend.Text = ""
End If
End Sub
98
End If
End Sub
99
100
}}
JSON.NextObject parses the string and returns a Map with the parsed data. This method
should be called when the top level value is an object (which is usually the case).
Now we will work with Map1 to get the required values.
We declare an additional Map with the name 'm'.
Code:
m = Map1.Get("menu")
101
102
Animation tutorial
103
104
105
animations(i).Duration = 500
Next
Last part if the usage of AnimationEnd event to start the next animation for Button6:
Code:
Sub Animation_AnimationEnd
If Sender = a6 Then
a7.Start(Button6)
Else If Sender = a7 Then
a8.Start(Button6)
Else If Sender = a8 Then
a9.Start(Button6)
End If
End Sub
This program looks really nice on a real device. It also works on the emulator but the
animations are less smooth.
106
Network tutorial
The Network library allows you to communicate over TCP/IP with other computers or
devices.
The Network library contains two objects. Socket and ServerSocket.
The Socket object is the communication endpoint. Reading and writing are done with
Socket.InputStream and Socket.OutputStream.
ServerSocket is an object that listens for incoming connections. Once a connection is
established an event is raised and a socket object is passed to the event sub. This socket
will be used to handle the new client.
Client application
Steps required:
- Create and initialize a Socket object.
- Call Socket.Connect with the server address.
- Connection is done in the background. The Connected event is raised when the
connection is ready or if it failed.
- Communicate with the other machine using Socket.InputStream to read data and
Socket.OutputStream to write data.
Server application
Steps required:
- Create and initialize a ServerSocket object.
- Call ServerSocket.Listen to listen for incoming connections. This happens in the
background.
- Once a connection is established the NewConnection event is raised and a Socket object
is passes.
- Call ServerSocket.Listen if you want to accept more connections.
- Using the Socket object received, communicate with the client.
We will see two examples.
The first example connects to a time server and displays the current date and time as
received from the server.
Code:
Sub Process_Globals
Dim Socket1 As Socket
End Sub
Sub Globals
End Sub
107
We are creating a new socket and trying to connect to the server which is listening on
port 13.
The next step is to wait for the Connected event.
If the connection is successful we create a TextReader object and initialize it with
Socket1.InputStream. In this case we want to read characters and not bytes so a
TextReader is used.
Calling tr.ReadLine may block. However we want to read at least a single line so it is
fine.
Then we read all the other available lines (tr.Ready means that there is data in the buffer).
In the second application we will create a file transfer application, that will copy files
from the desktop to the device.
The device will use a ServerSocket to listen to incoming connections.
108
Once a connection has been made, we will enable a timer. This timer checks every 200ms
whether there is any data waiting to be read.
The file is sent in a specific protocol. First the file name is sent and then the actual file.
We are using a RandomAccessFile object to convert the bytes read to numeric values.
RandomAccessFile can work with files or arrays of bytes, we are using the later in this
case.
RandomAccessFile can be set to use little endian byte order. This is important here as the
desktop uses this byte order as well.
The desktop example application was written with Basic4ppc.
Once connected the user selects a file and the file is sent to the device which saves it
under /sdcard/android.
Both applications are attached.
Some notes about the code:
- The server is set to listen on port 2222.
The server displays its IP when it starts. The desktop client should use this IP address
when connecting to a real device (this IP will not work with the emulator).
However if you work with the emulator or if your device is connected to the computer in
debug mode you can use 'adb' to forward a desktop localhost port to the device.
This is done by issuing "adb forward tcp:5007 tcp:2222"
Now in the client code we should connect to the localhost ip with port 5007.
Code:
Client.Connect("127.0.0.1", 5007)
Again if you are testing this application in the emulator you must first run this adb
command. Adb is part of the Android SDK.
- Listening to connections:
Code:
Sub Activity_Resume
ServerSocket1.Listen
End Sub
Sub Activity_Pause (UserClosed As Boolean)
If UserClosed Then
Timer1.Enabled = False
Socket1.Close
ServerSocket1.Close 'stop listening
End If
End Sub
Sub ServerSocket1_NewConnection (Successful As Boolean, NewSocket As
Socket)
109
If Successful Then
Socket1 = NewSocket
Timer1.Enabled = True
InputStream1 = Socket1.InputStream
OutputStream1 = Socket1.OutputStream
ToastMessageShow("Connected", True)
Else
Msgbox(LastException.Message, "Error connecting")
End If
ServerSocket1.Listen 'Continue listening to new incoming
connections
End Sub
110
Regular Expressions
Regular expressions are very powerful and make complicate parsing challenges much
easier.
This short tutorial will describe the usage of regular expressions in Basic4android.
If you are not familiar with regular expressions you can find many good tutorials online. I
recommend you to start with this one: Regular Expression Tutorial - Learn How to Use
Regular Expressions
Basic4android uses Java regular expression engine. See this page for specific nuances
related to this engine: Pattern (Java Platform SE 6)
Regular expressions methods in Basic4android start with the predefined object named
Regex. You can write Regex followed by a dot to see the available methods.
All methods accept a pattern string. This is the regular expression pattern. Note that
internally the compiled patterns are cached. So there is no performance loss when using
the same patterns multiple times.
For each method there are two variants. The difference between the variants is that the
second one receives an 'options' integer that affects the engine behavior. For now there
are two option, CASE_INSENSITIVE and MULTILINE. CASE_INSENSITIVE makes
the pattern matching be case insensitive. MULTILINE changes the string anchors ^ and
& match the beginning and end of each line instead of the whole string.
Both options can be combined by calling Bit.Or(Regex.MULTILINE,
Regex.CASE_INSENSITIVE).
Matching the whole string
IsMatch and IsMatch2 are good to validate user input. The result of these methods is true
if the whole string matches the pattern.
For example the following code checks if a date string is formatted in a format similar to:
12-31-2010
Code:
Log(Regex.IsMatch("\d\d-\d\d-\d\d\d\d", "11-15-2010")) 'True
Log(Regex.IsMatch("\d\d-\d\d-\d\d\d\d", "12\31\2010")) 'False
111
Split and Split2 splits a text around matches of the given pattern.
Simple case:
Code:
Dim data As String
data = "123,432,13,4,12,534"
Dim numbers() As String
numbers = Regex.Split(",", data)
Dim l As List
l.Initialize2(numbers)
Log(l)
Lists can be easily printed with Log so we add the array to the list.
The result is:
The comma followed by a single space is part of the list formatting. The expected values
were parsed.
Now if the data value was "123, 432 , 13 , 4 , 12, 534"
The result wasn't perfect:
There are extra spaces which are part of the parsed values.
We can change the pattern to match a comma or white space:
Code:
numbers = Regex.Split("[,\s]", data)
112
The groups feature is very useful. If you find yourself calling String.IndexOf together
with String.Substring multiple times, it is a good hint that you should move to a Regex
and Matcher.
113
114
When download completes we also check if the activity is currently active. If not we
show a status bar notification that notifies the user that download has completed. When
the user presses on the notification (by first dragging the status bar downwards) our
activity is resumed.
The program is attached.
115
AsyncStreams tutorial
A new object type is available in the RandomAccessFile library named AsyncStreams.
AsyncStreams allows you to read data from an InputStream and write data to an
OutputStream without blocking your program. The reading and writing are done with two
separate threads.
When new data is available the NewData event is raised with the data.
When you write data to the OutputStream the data is added to an internal queue and then
sent in the background.
AsyncStreams is very useful when working with slow streams like network streams or
Bluetooth streams.
If you work with such streams using the main thread your program may hang or block
while waiting for a value.
The way we tried to deal with it in the Serial tutorial and Network tutorial is with a Timer
that checks whether there are bytes waiting in the buffer. However even if there are bytes
available there is a chance that not enough are available and then our program will hang
or block until those are available.
Using AsyncStreams is usually simpler and safer.
AsyncStreams can work in two modes. Regular mode and "prefix mode". Both work as
described above.
The following code demonstrates a simple program that sends text to a connected device
or computer:
Code:
Sub Process_Globals
Dim AStreams As AsyncStreams
Dim Server As ServerSocket
Dim Socket1 As Socket
End Sub
Sub Globals
Dim EditText1 As EditText
End Sub
Sub Activity_Create(FirstTime As Boolean)
If FirstTime Then
116
Server.Initialize(5500, "Server")
Server.Listen
Log("MyIp = " & Server.GetMyIP)
End If
EditText1.Initialize("EditText1")
EditText1.ForceDoneButton = True
Activity.AddView(EditText1, 10dip, 10dip, 300dip, 60dip)
End Sub
Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
If Successful Then
ToastMessageShow("Connected", False)
Socket1 = NewSocket
AStreams.InitializePrefix(Socket1.InputStream, False,
Socket1.OutputStream, "AStreams")
Else
ToastMessageShow(LastException.Message, True)
End If
Server.Listen
End Sub
Sub AStreams_NewData (Buffer() As Byte)
Dim msg As String
msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
ToastMessageShow(msg, False)
Log(msg)
End Sub
Sub AStreams_Error
ToastMessageShow(LastException.Message, True)
End Sub
'press on the Done button to send text
Sub EditText1_EnterPressed
If AStreams.IsInitialized = False Then Return
If EditText1.Text.Length > 0 Then
Dim buffer() As Byte
buffer = EditText1.Text.GetBytes("UTF8")
AStreams.Write(buffer)
EditText1.SelectAll
Log("Sending: " & EditText1.Text)
End If
End Sub
Sub Activity_Pause(UserClosed As Boolean)
If UserClosed Then
Log("closing")
AStreams.Close
Socket1.Close
End If
117
End Sub
Then when there is new data available we convert the bytes to string and show it:
Code:
Sub AStreams_NewData (Buffer() As Byte)
Dim msg As String
msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
ToastMessageShow(msg, False)
End Sub
When the user enters text in the EditText, the text is being sent:
Code:
Sub EditText1_EnterPressed
If AStreams.IsInitialized = False Then Return
If EditText1.Text.Length > 0 Then
Dim buffer() As Byte
buffer = EditText1.Text.GetBytes("UTF8")
AStreams.Write(buffer)
EditText1.SelectAll
Log("Sending: " & EditText1.Text)
End If
End Sub
Prefix mode
When the object is initialized in prefix mode the data is expected to adhere to the
following protocol: every message (bytes array) should be prefixed with the bytes array
length (as an Int). So if another device sends us a message made of 100 bytes. The stream
is expected to include 4 bytes with a value of 100 and then the 100 bytes.
The NewData event will be raised with the 100 bytes. It will not include the 4 prefix
bytes.
When you send data with Write or Write2 the bytes length will be added automatically as
the prefix of this message.
If you can work in prefix mode, which is usually only possible if you are implementing
both sides, it is highly recommended to do so. Under the cover AsyncStreams uses the
message length prefix to make sure that the NewData event is always raised with
complete messages. If for example it expects 100 bytes and only 60 bytes arrived, it will
wait till the other 40 bytes arrive. In regular mode the event will be raised twice and you
will need to handle the two parts of the message.
AsyncStreams also handles the case where 100 bytes are expected and more than 100
bytes arrived (which means that there are several messages).
118
The Error event will be raised if there is any error. You can use LastException to find the
reason for the error.
119