The entrywidget
module provides the EntryWidget class, a widget that
allows the user to enter a place name for a location and save it to a file.
We begin by importing the classes we use from the standard libraries.
from java.lang import String
from java.io import File, FileWriter
from java.text import SimpleDateFormat
from java.util import Date
from android.location import Location
from android.os import Environment
from android.view import View
from android.widget import Button, EditText, LinearLayout, TextView, Toast
The widget is derived from the LinearLayout class, which means that we can use it as a container to automatically position the views we want to display.
class EntryWidget(LinearLayout):
__interfaces__ = [View.OnClickListener]
Since the widget will contain a Button, we declare that it implements an interface that allows us to respond to clicks.
The widget will have a Location object associated with it. Initially, the
object will be null, but since we cannot create a null Location using a
constructor we need a way of declaring the type of the location
field and
we do this by explicitly declaring it and setting it to None
.
__fields__ = {"location": Location}
The initialisation method calls the base class's implementation before configuring its orientation and setting up the user interface.
def __init__(self, context):
LinearLayout.__init__(self, context)
self.setOrientation(self.VERTICAL)
We store the context for later use and initialise the field containing the location as described above.
self.context = context
self.location = None
The rest of the method is concerned with setting up the user interface, registering the widget itself as the listener for the button.
label = TextView(context)
label.setText("Place name:")
self.textEdit = EditText(context)
self.saveButton = Button(context)
self.saveButton.setText("Save location")
self.saveButton.setOnClickListener(self)
self.addView(label)
self.addView(self.textEdit)
self.addView(self.saveButton)
We reimplement the setEnabled method to allow us to change the states of the child views, enabling and disabling them to match the state of the widget itself.
def setEnabled(self, enabled):
i = 0
while i < self.getChildCount():
self.getChildAt(i).setEnabled(enabled)
i += 1
We provide the setLocation method to allow the main activity class to pass new locations to the widget, ensuring that it is enabled as soon as it has a location it can save.
@args(void, [Location])
def setLocation(self, location):
self.location = location
self.setEnabled(True)
We implement the onClick method as required by our declaration of the View.OnClickListener interface. The method is called when the button is clicked.
def onClick(self, view):
f = self.createFile()
if f == None:
return
We call a custom method to obtain a File object that we intend to write to. If this method fails, perhaps because there is no external storage to use, we return immediately.
We obtain the string from the EditText widget in the widget, noting
that we have to cast the widget to TextView in order to access the
getText
method.
placeName = str(CAST(self.textEdit, TextView).getText())
We use the SimpleDateFormat class to create a string containing the current date and time.
dateString = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())
We open the file for writing and appending before adding a line to the end of it containing the date, coordinates and place name.
stream = FileWriter(f, True)
stream.write(
dateString + " " + \
self.location.convert(self.location.getLatitude(),
Location.FORMAT_DEGREES) + " " + \
self.location.convert(self.location.getLongitude(),
Location.FORMAT_DEGREES) + " " + \
placeName + "\n"
)
We flush and close the file to ensure that the data is written safely.
stream.flush()
stream.close()
Finally, we report that the location has been saved using a transient notification.
Toast.makeText(self.context, "Location saved in " + f.getAbsolutePath(), Toast.LENGTH_SHORT).show()
The createFile method is a simple utility method that creates and returns a file in the device's external storage area, creating a directory to hold the file if required.
@args(File, [])
def createFile(self):
if Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED:
return None
If no external storage is mounted then return None immediately to indicate failure.
We obtain the directory on the external storage device that is used to store downloads.
storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS)
If possible, we create a subdirectory for this example, returning None to indicate failure.
subdir = File(storageDir, "GPSFile")
if not subdir.exists():
if not subdir.mkdirs():
return None
return File(subdir, "locations.txt")