Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/.classpath
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/.classpath	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/.classpath	(revision 24268)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="lib" path="jars/mapsforge-map-0.2.1.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/.project
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/.project	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/.project	(revision 24268)
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>Tipple</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/AndroidManifest.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/AndroidManifest.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/AndroidManifest.xml	(revision 24268)
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.greenstone.android.tipple"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="8" />
+	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    
+    <application android:icon="@drawable/tipple_eab_icon" android:label="@string/app_name">
+        <activity android:name=".TippleActivity"
+                  android:label="@string/app_name"
+                  android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+		<activity android:name=".EditPreferences" />
+		<activity android:name="TippleLogFileBrowser" />
+		<activity android:name=".InfoView" android:theme="@android:style/Theme.NoTitleBar" />
+		
+    </application>
+</manifest>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/assets/geodata/hamilton.loc
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/assets/geodata/hamilton.loc	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/assets/geodata/hamilton.loc	(revision 24268)
@@ -0,0 +1,550 @@
+<?xml version="1.0" encoding="UTF-8" ?> 
+<tour>
+
+<location>
+  <latitude>-37.80608675</latitude>
+  <longitude>175.3029395</longitude>
+  <radius>0.01</radius>
+  <title>Beginning of gardens</title>
+  <text>
+    Welcome to the Hamilton Gardens. Hamilton Gardens is not a
+    traditional botanic garden. We like to say, that while botanic
+    gardens are collections of plants, Hamilton Gardens is a
+    collection of gardens. So we have taken lots of different kinds of
+    garden designs and collected them together in one place. We have
+    organized those gardens into five collections: Paradise Garden
+    collection, The Productive Garden collection, Fantasy garden
+    collection, Cultivar Garden collection and a landscape garden
+    collection. Hamilton Gardens itself started in 1964 and was
+    developed in kind of a hodgepodge sort of way until the 1980s when
+    a new overall plan was laid out.
+  </text>
+</location>
+
+<location>
+  <latitude>-37.806534</latitude>
+  <longitude>175.30264764</longitude>
+  <radius>0.01</radius>
+  <title>Beginning of paradise garden</title>
+  <text>
+    You are now at the paradise gardens. The reason why they're called
+    paradise gardens is because they all follow idea that a garden is
+    a kind of a paradise, so it's a refuge or retreat from the
+    everyday world. So they're all small walled gardens, enclosed
+    gardens. The word paradise comes from an old Persian word that
+    means enclosed garden. We have brought together here different
+    designs from different eras and different places around the
+    world. The paradise gardens are the most well known ones of all
+    our garden collections. We have used actual classic garden designs
+    that have been highly influential on gardens.
+  </text>
+</location>
+<location>
+  <latitude>-37.806782</latitude>
+  <longitude>175.3030759</longitude>
+  <radius>0.007</radius>
+  <title>Egyptian Court</title>
+  <text>
+    These are two statues that were first erected when the area was developed/ 
+    They're Egyptian to recognize the origin
+    of gardening in the Egyptian Nile Delta.
+  </text>
+</location>
+
+<location>
+  <latitude>-37.8066662</latitude>
+  <longitude>175.30305895</longitude>
+  <radius>0.005</radius>
+  <title>Japanese Garden</title>
+  <text>
+This is a Japanese Garden of Contemplation. There are certain key elements to look out for here, such as the kind of paradise element; this is a miniaturised, a very abstract version of nature. So everything here is very tightly controlled, and yet, represents something in nature. So some people like to think of this as a miniature landscape. You might say that the moss here is like fields, you know like we're flying across the fields. 
+The other thing to look out for in the Japanese Garden of Contemplation is the rocks and the rock placement. Have a look at the three rocks. When you look at them they don't look like much special but they are a classic kind of Confucian arrangement; where the two little rocks are bowing to the big rock there, which tells you something about respect for authority and respect to your elders. So there's coded messages in the garden, which it gives you a lot of pleasure and is fun to try and deconstruct or decode.
+
+The Japanese garden tries to create something like an abstract version of nature. For example, if  look at the trees. See how the branches are all trained to be straight out there and the trunk is curved; you look at very, very old pine trees they actually do that by themselves. But this is a very young pine tree but it's trained to look old. We don't want something that's a hundred feet tall because that would be out of scale with the garden. So Japanese gardens are a bit like Bonsai: it's trained to look very, very old and very big, but actually they're quite young and they're quite small.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8065514565</latitude>
+  <longitude>175.3029906750</longitude>
+  <radius>0.014</radius>
+  <title>Japanese Rock Garden</title>
+  <text>
+A Japanese garden is not all revealed to you in one grand moment. It's sort of revealed to you in stages. This part is often called the Zen garden. Its proper name is karesansui which means dry landscape or water mountain landscape. Again, obviously the rock placement is crucial. It's not something that we're necessarily trained to see. There are never any flowers in here; they're always pruned off. The classic kind of interpretation of this landscape is that it is a shoreline. So the gravel is the water and the swirling patterns of the current and then we have the headland from the islands and so on. 
+
+There are no flowers as it is thought that bright colours will disturb the tranquillity of the views. It's supposed to be very calming. The background is left blank; so it's as if they're drawn on a blank sheet of paper, and they are a bit like the landscapes that are drawn on silk screen scrolls. They use a lot of negative space when they're painting. 
+
+A similar effect can be seen when looking at the water. The rocks up close to us a really big and all the rocks on the far shore are kind of small, which it accentuates the distance.  So again it's a vast landscape in miniature, collected here for us. The Japanese garden designers take a lot of care to replicate the patterns in nature; the way that the water arose, the land and the way that the trees grow over the water. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8066596</latitude>
+  <longitude>175.3033258</longitude>
+  <radius>0.007</radius>
+  <title>Entrance to English Garden</title>
+  <text>
+Now we jump fast forward into the late 1800's; maybe early 20th Century and into England. There was a kind of back to nature type drive happening in response to all the industrialisation and things that was happening in England at the time. 
+What we've done here is we've taken three classic kind of garden designs, all based on layouts that were done by Gertrude Jekyll and her friend Lutyen. There is a long border, the collector's  garden and a white garden.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8065417</latitude>
+  <longitude>175.3033681</longitude>
+  <radius>0.005</radius>
+  <title>Long Border</title>
+  <text>
+The long border is just simply what it says, it's just a big long path with flowers either side; mostly annuals and some perennials and mixed border, as they say. The warm colours are in the middle and the cool colours are at the end. It has the reds and the oranges and the yellows around the middle, and then at the end all the blues and the more pastely, pinky, bluey things. It's actually quite carefully put together. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.806508</latitude>
+  <longitude>175.30349805</longitude>
+  <radius>0.007</radius>
+  <title>Collector's Garden</title>
+  <text>
+This is an English collector's garden. This is the garden that the plant collectors really like. By this stage in European garden history, lots and lots of plants have been collected from all around the world. People went to all sorts of interesting places and bring back all these plants, and growing them in their gardens. So, while the other kind of garden traditions are based a lot on what was available natively in that location at that time; this is one of those gardens that brings lots of interesting planting material together. This is a copy of a pavilion that exists that Millmead house. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80661782</latitude>
+  <longitude>175.3035174</longitude>
+  <radius>0.007</radius>
+  <title>White Garden</title>
+  <text>
+There's not just white flowers in here; there is also silver foliage like one the big trees. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.806797</latitude>
+  <longitude>175.3035393</longitude>
+  <radius>0.004</radius>
+  <title>Entrance to Chinese Garden</title>
+  <text>
+This is the Chinese Scholar's garden. This is the oldest one from all the paradise gardens. They go right back to second century if we like; although lots of these design elements were common to lots of different viewers. This garden tells the story of a life cycle. Along with the Japanese garden tradition, the Chinese gardeners collected rocks. And one of the things that the rocks connects to is those Chinese scrolls and the scroll art, and those paintings they would do of those amazing kind of mythical mountain ranges where the immortals were supposed to dwell. So this has been a miniature. Again we've got these big blank walls there with the rocks against them; so the classic opening. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8067807</latitude>
+  <longitude>175.3039754</longitude>
+  <radius>0.005</radius>
+  <title>The Entrance</title>
+  <text>
+One of the interesting things about this garden is the contrast, so this is the dark area; its covered in Jasmine and it smells really good when it's in flower. So there's dark and then there's light inside and outside and so on. So in terms of that story of that lifecycle, I guess that's kind of birth. 
+Again, there's a kind of representation of nature here that much less restrained and much less abstract  than the Japanese version. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80675698</latitude>
+  <longitude>175.3039148</longitude>
+  <radius>0.004</radius>
+  <title>Tree, Rock and Bridge</title>
+  <text>
+The rock in the enclosure there came from the bottom of Lake Taihu in China. It got shipped over here. The gardeners putt a different bonsai three there every month.
+	The bridge is not straight to stop the dragons coming across it. They're also dragon shapes represented on the top of those walls. In this garden you can see plants native to china. For the Chinese certain plants had strong associations. For example, the bamboo represented uprightness and strength. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8066641092</latitude>
+  <longitude>175.3039830923</longitude>
+  <radius>0.005</radius>
+  <title>Monk in Grotto</title>
+  <text>
+The cave holds a littel statue of a monk.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80680524</latitude>
+  <longitude>175.30421622</longitude>
+  <radius>0.005</radius>
+  <title>The High Point</title>
+  <text>
+This point symbolises the high point of your life; you get to this point and then you stand up here and you survey where you've been. It is a restful garden because there is a lot of green and not many flowers, just some shade. And at the end of the path, you find yourself back where you began.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80686102</latitude>
+  <longitude>175.30338661</longitude>
+  <radius>0.005</radius>
+  <title>Entrance to Modernist Garden</title>
+  <text>
+This is the modernist garden based on the designs of Thomas Church; most of his famous designs are in California. This garden represents a domestic backyard. All these plants here are specifically native plants. In America, gardens use American trees and American plants; the ones we used were mostly from Southern California. Some of them come from the east coast but most of them come from the west coast. 
+
+The design is very modernist; there's no symmetry and everything's kind of curvy. All the materials are kind of space age materials of the time. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80699413</latitude>
+  <longitude>175.30360142</longitude>
+  <radius>0.007</radius>
+  <title>American House</title>
+  <text>
+The intention when they built this garden was to symbolize the house with ranch sliders and one looks onto the back yard. Of course we can't build a whole house there so we just had a draft of it.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80688375</latitude>
+  <longitude>175.30317334</longitude>
+  <radius>0.005</radius>
+  <title>Indian Garden</title>
+  <text>
+
+The story behind this Indian garden is that we call it the Indian Shaba garden but it's really Persian. So the Persians came from Mongolia; conquered a whole lot of Asia and Western Europe and then settled down in Persia. After a few generations the Persians went eastwards and conquered a lot of northern Indian. They had come from a really dry, arid mountainous region; so for them, water and irrigation was really, really important, and you didn't waste it on big massive fountains. It was going to be really subtle, and it was going to serve the purpose of irrigation as well. By the time they had conquered India, the terrain was a lot flatter and water was a lot more plentiful, but they retained these kind of subtle elements. So there are irrigation channels and little bubbly fountains and water rills. So it's a classic case of something that started off as a design necessity; A couple of centuries later it was like a design exhibit; it's no longer necessary but people use it anyway.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80701817</latitude>
+  <longitude>175.30314905</longitude>
+  <radius>0.015</radius>
+  <title>Flower Beds</title>
+  <text>
+These four gardens get completely dug up twice a year, replace it with all new plants.  The gardeners don't use a lot of sprays but are very, very careful in their plant selections. They use a lot of original species rather than hybrids. Changing all the plants takes them a whole week, the entire workforce of Hamilton Gardens. We close it off and they just distribute them out and replant it. Because the garden designers were Muslims and had a restriction on representations of a human and animal figures, the designs that you see are often abstracts of plants.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8071147203</latitude>
+  <longitude>175.3031462431</longitude>
+  <radius>0.010</radius>
+  <title>River View</title>
+  <text>
+The director of Hamilton Gardens, Peter Sergel, who designed these gardens; took a trip to India and took photos of fountains that look exactly like that. He brought the photos home and he had guys reproduce them like that, but they're very close to the original thing. These are kinds of gardens that would have been on the banks of the river.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80681071</latitude>
+  <longitude>175.30282743</longitude>
+  <radius>0.012</radius>
+  <title>Italian Renaissance Garden</title>
+  <text>
+This is the Italian Renaissance garden; so obviously it comes from the Renaissance period, a rebirth of culture, coming out of the dark ages in Europe, and specifically in Italy where it starts in Florence. So you had a coming together of lots of different historical forces. 
+
+You had this concentration of extreme wealth firstly, and that was partially because of the Catholic church had its headquarters, and it was taking a lot of money from the rest of Europe. You had an increased scientific knowledge and increased humanistic rationalism coming along, and with that there was a huge opening up of trades. 
+
+This is quite a big garden by our standards. It is based on this small private side garden of a much, much bigger garden complex in Italy. The Italian merchants were quite wealthy and they spent money on their gardens. At the same time there's an increased interest in antiquity; so part of their Roman heritage and kind of the Roman Empire and so on. So there's a really interesting congruent here between a new rationalistic, scientific view of the world and a Christian catholic view of the world, and a pagan classical view of the world; all that is coming together there. For example, the water feature is a pagan Romulus and Remus statue. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8068357706</latitude>
+  <longitude>175.3027600050</longitude>
+  <radius>0.004</radius>
+  <title>Overview of Italian Garden</title>
+  <text>
+There's two kinds of key overall layout aspects. One is just the fact that there are three areas. So there's the outside bosco area beyond the garden in the forest, the untamed wilderness from which only the beasts live, and humans came from there but we don't live there anymore. The second area is the  orchards, the prater with its fruit trees and grapevines, and then the third one is the bottom part, which is the formal part of the garden. 
+
+Cicero talks about the fear of nature. There is the first nature, which is the untamed wilderness. The second nature, which is the farming, and then the third nature which is the garden. Third nature is nature plus art; whereas farming and gardening for food is nature plus science or functional behaviour. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80698196</latitude>
+  <longitude>175.30278038</longitude>
+  <radius>0.005</radius>
+  <title>Water in the Italian Garden</title>
+  <text>
+Another layout aspect is the progression of water. There are actually little nozzles in the wall next to the grotto and they make this lovely little mist and this is a grotto that represents the female and fertile. Beyond that there's some little fountains that go down underneath it and then there's the big fountain in the middle; it spurts up, and it's a much more masculine. And then beyond that there's the mighty river. 
+
+Obviously, it is a highly symmetrical garden area. You may notice that from each garden you can't see any of the other gardens. This garden and the Indian garden are really good examples of what garden designers do; which is that they borrow scenery, they borrow the landscape from outside. So, if your neighbour has a really big, lovely oak tree; you can build that into your design. And so here's a great example. The river is not part of Hamilton gardens but it certainly makes a good impact when you come out here and see it. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80704606</latitude>
+  <longitude>175.3024579</longitude>
+  <radius>0.006</radius>
+  <title>Medici Court</title>
+  <text>
+This is the Medici court; fantastic for outdoor theatre and so on, and the Medici gallery out there which is a little patio area.   </text>
+  <audio>
+  </audio>
+</location>
+
+<!-- maori gardens -->
+
+<location>
+  <latitude>-37.8068084</latitude>
+  <longitude>175.30235491</longitude>
+  <radius>0.005</radius>
+  <title>Tainui Landing</title>
+  <text>
+This is our newest garden and its consequently the one that we feel most proud of. It's called Te Parapara. Te Parapara is a traditional Maori garden of a kind. It's not a recreation of existing or historical Maori gardens as much as the Paradise gardens are, but it is much more of a narrative garden. It basically tells a story of the establishment of cultivated food crops in New Zealand. 
+
+The story begins at the gate; which represents the landing of Tainui waka in New Zealand in a kind of landfall. On the right hand side after the gate there is a pomaderris Tainui tree, big and tall. On the left is a little Pohutekawa tree. The Pohutukawa represents the tree that the Tainui waka was tied to when it first was landed at Tawhia. The Pomaderris represents the floor boards, and in the story of the landing of Tainui, the floor boards took root. We know that this is mythical because pomaderris was native. 
+  </text>
+
+<tts>
+This is our newest garden and its consequently the one that we feel most proud of. It's called Tee Paaraa-paaraa. Tee Paaraa-paaraa is a traditional Maori garden of a kind. It's not a recreation of existing or historical Maori gardens as much as the Paradise gardens are, but it is much more of a narrative garden. It basically tells a story of the establishment of cultivated food crops in New Zealand. 
+
+The story begins at the gate; which represents the landing of Tye-newee waakaa in New Zealand in a kind of landfall. On the right hand side after the gate there is a Poh-maadeerrees Tye-newee tree, big and tall. On the left is a little Poh-hootee-kawaa tree. The Poh-hootee-kawaa represents the tree that the Tye-newee waakaa was tied to when it first was landed at Kefeea. The Poh-maadeerrees represents the floor boards, and in the story of the landing of Tye-newee, the floor boards took root. We know that this is mythical because Poh-maadeerrees was native. 
+
+</tts>
+
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80666313</latitude>
+  <longitude>175.30244908</longitude>
+  <radius>0.005</radius>
+  <title>Hoturoa Statue</title>
+  <text>
+The statue here is Hoturoa and he was the captain of the Tainui waka. He is carved in a Tahitian style to represent the fact that there was no kind of indigenous New Zealand art; they were all from the islands. And so all these plants are the plants that we here natively when the waka began arriving. Lots of these native plants were used as foods or textiles, or other kinds of useful things by the early Maori. They discovered that these plants had medicinal uses or you could eat them. 
+
+  </text>
+<tts>
+The statue here is Hotooro-aa and he was the captain of the Tye-newee waakaa. He is carved in a Tahitian style to represent the fact that there was no kind of indigenous New Zealand art; they were all from the islands. And so all these plants are the plants that we here natively when the waka began arriving. Lots of these native plants were used as foods or textiles, or other kinds of useful things by the early Maori. They discovered that these plants had medicinal uses or you could eat them. 
+
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80704729</latitude>
+  <longitude>175.3022264</longitude>
+  <radius>0.009</radius>
+  <title>Gate</title>
+  <text>
+This structure represents an entrance way to a Pa or a Marae. The hut on the left of the entrance is a koutou which is a traditional food preparation structure and is outside because it's not tapu. Food preparation is not tapu; its noa, so it's not sacred. 
+
+The figures on the gate tell the story of the discovery of Oka, the use of ochre to preserve timber and preserve carvings. The ochre is made with a kind of iron rich clay, mineral rich clay that is often found in stream beds. There are lots of different colours: the red that you see most places to a yellow, a black and a white. It is mixed with fat which soaks into the timber and helps preserve the timber. Often times the coastal tribes would use shark fat. 
+
+Coming back to the figures on the gate, they tell the story of a man who was married and then the patupaiarehe, which are like the fairy people; came and stole his wife. He went to look for his wife and he couldn't ever find her because she would be there in the mist and then as soon as he went to get her she would disappear; so he could never get her back. So he went to see a Priest and the Priest said, &quot;You draw a circle around yourself with a mixture of kumara and ochre; smear it on the ground and then when she comes you'll embrace her and it will scare off the patupaiarehe&quot;. And the man did as he was told, and it worked. They say that the kind of the patupaiarehe who wanted to marry her still lives at the top of Mount Pirongia when the mist comes over that mountain. She is shown on the right hand side, her husband on the left side and the fairy people up the top. 
+
+The figures with square shaped faces represent Matariki, which is the Pleiades. This is the constellation that is quite important for timing the kumara harvest. For the Greeks it was seven sisters but for a Maori it's a woman and six daughters.
+  </text>
+<tts>
+This structure represents an entrance way to a Paa or a Ma-rye. The hut on the left of the entrance is a koutou which is a traditional food preparation structure and is outside because it's not tapoo. Food preparation is not tapoo; its noa, so it's not sacred. 
+
+The figures on the gate tell the story of the discovery of Oka, the use of ochre to preserve timber and preserve carvings. The ochre is made with a kind of iron rich clay, mineral rich clay that is often found in stream beds. There are lots of different colours: the red that you see most places to a yellow, a black and a white. It is mixed with fat which soaks into the timber and helps preserve the timber. Often times the coastal tribes would use shark fat. 
+
+Coming back to the figures on the gate, they tell the story of a man who was married and then the Paatoopai-aree, who are a kind of fairy people; came and stole his wife. He went to look for his wife and he couldn't ever find her because she would be there in the mist and then as soon as he went to get her she would disappear; so he could never get her back. So he went to see a Priest and the Priest said, &quot;You draw a circle around yourself with a mixture of koomera and ochre; smear it on the ground and then when she comes you'll embrace her and it will scare off the Paatoopai-aree. And so the man did as he was told, and it worked. They say that the kind of the Paatoopai-aree who wanted to marry her still lives at the top of Mount Peeron-keaa when the mist comes over that mountain. She is shown on the right hand side, her husband on the left side and the fairy people up the top. 
+
+The figures with square shaped faces represent Maataareekee, which is the Pleiades. This is the constellation that is quite important for timing the koomera harvest. For the Greeks it was seven sisters but for a Maori it's a woman and six daughters.
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80718379</latitude>
+  <longitude>175.30205507</longitude>
+  <radius>0.008</radius>
+  <title>Koomera</title>
+  <text>
+This is the realm of the cultivated food crops. They were brought here on the waka; they're not native to New Zealand, and there's actually just recently been a really important archaeological discovery on the west coast of the South American continent of a chicken bone that proves contact between Polynesians and south Americans. The Polynesians came to south America. When Columbus got to South America and discovered that there were chickens there. They didn't know why and at the same time Polynesians have got kumara, and kumara is native to south America. It doesn't appear in Europe; it's a new world crop. So there are theories that Polynesians came from Taiwan or they came from the old world. It leaves that question: Where did they get the kumara from? 
+
+The kumara we've got growing in this area was the most important crop for Maori. It was the  number one source of carbohydrate for them. All other the native plants together did not provide any productive capacity of this kind; the ease of its cultivation and so on. Kumara doesn't set seed in New Zealand because it is too cold. So every season they have to sort out the tubas that they're going to keep for the next year's planting and the tubas they're going to eat.
+
+	The maori accumulated a vast knowledge on this problem of how to make sure that they have got enough kumara for the next crop. When the European explorers got here they had this huge plantation of really well organized plantations of kumara. 
+
+So this entire garden is now set up for kumara planting in a central position. All kumara in this garden gets harvested and the first bit gets presented to the Maori King and then the rest of it gets eaten in a big hangi. The Hamilton Gardens grow at least two of the original four varieties from pre-European times. When Europeans got here and brought bigger, better kumara from South America, the Maori gardeners started using those as they yield bigger tubers. The old types of kumara got lost and then in the 1960s or 1970s the Crown science people were looking for the old varieties but could not find them anywhere. They had to go to a Japanese scientist who had been out here in New Zealand and got tubers to store them in Japan. So these old types that we have growing here owe their existence to a Japanese scientist.
+
+You will see that each plant is planted in a mound. The mound is there for lots of reasons. It increases the amount of sunlight that hits the ground, to keep it warmer for longer; especially when the sun gets low. You can't plant kumara until November and so it's really crucial to keep the end of the season as far away as possible. April, May; and the sun is getting quite low in the sky. Another aspect is increased drainage so the tubas don't rock. It also provides soil improvement and Maori gardening sites are usually  discovered by the existence of borough pits, basically big holes in the ground where good soil; which is usually very, very sandy; very pumicey, would be dug up and watered. So the soil you see here is full of rocks and pumice and sand to make it much better draining than it otherwise would be. So we find very advanced soil improvement techniques that are  certainly far beyond hunter gatherer type societies. 
+
+  </text>
+
+<tts>
+This is the realm of the cultivated food crops. They were brought here on the waakaa; they're not native to New Zealand, and there's actually just recently been a really important archaeological discovery on the west coast of the South American continent of a chicken bone that proves contact between Polynesians and south Americans. The Polynesians came to south America. When Columbus got to South America and discovered that there were chickens there. They didn't know why and at the same time Polynesians have got koomera, and koomera is native to south America. It doesn't appear in Europe; it's a new world crop. So there are theories that Polynesians came from Taiwan or they came from the old world. It leaves that question: Where did they get the koomera from? 
+
+The koomera we've got growing in this area was the most important crop for Maori. It was the  number one source of carbohydrate for them. All other the native plants together did not provide any productive capacity of this kind; the ease of its cultivation and so on. Koomera doesn't set seed in New Zealand because it is too cold. So every season they have to sort out the tubas that they're going to keep for the next year's planting and the tubas they're going to eat.
+
+	The maori accumulated a vast knowledge on this problem of how to make sure that they have got enough koomera for the next crop. When the European explorers got here they had this huge plantation of really well organized plantations of koomera. 
+
+So this entire garden is now set up for koomera planting in a central position. All koomera in this garden gets harvested and the first bit gets presented to the Maori King and then the rest of it gets eaten in a big hangi. The Hamilton Gardens grow at least two of the original four varieties from pre-European times. When Europeans got here and brought bigger, better koomera from South America, the Maori gardeners started using those as they yield bigger tubers. The old types of koomera got lost and then in the 1960s or 1970s the Crown science people were looking for the old varieties but could not find them anywhere. They had to go to a Japanese scientist who had been out here in New Zealand and got tubers to store them in Japan. So these old types that we have growing here owe their existence to a Japanese scientist.
+
+You will see that each plant is planted in a mound. The mound is there for lots of reasons. It increases the amount of sunlight that hits the ground, to keep it warmer for longer; especially when the sun gets low. You can't plant koomera until November and so it's really crucial to keep the end of the season as far away as possible. April, May; and the sun is getting quite low in the sky. Another aspect is increased drainage so the tubas don't rock. It also provides soil improvement and Maori gardening sites are usually  discovered by the existence of borough pits, basically big holes in the ground where good soil; which is usually very, very sandy; very pumicey, would be dug up and watered. So the soil you see here is full of rocks and pumice and sand to make it much better draining than it otherwise would be. So we find very advanced soil improvement techniques that are  certainly far beyond hunter gatherer type societies. 
+
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80716643</latitude>
+  <longitude>175.30177131</longitude>
+  <radius>0.004</radius>
+  <title>Mulberry Tree</title>
+  <text>
+This is paper mulberry which can be used to make a type of cloth out of it. During the summer it grows like a weed. In the winter time it just dies down. In Northland it grows for the whole year but where there's a frost, it will die back. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8072314</latitude>
+  <longitude>175.30184732</longitude>
+  <radius>0.005</radius>
+  <title>Taro Plant</title>
+  <text>
+This one is a Taro plant. You can eat the leaves and its root.
+  </text>
+
+  <tts>
+This one is a Taaro plant. You can eat the leaves and its root.
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8071683645</latitude>
+  <longitude>175.3020411730</longitude>
+  <radius>0.014</radius>
+  <title>Three Houses</title>
+  <text>
+
+You can see three structures here. They are really interesting and the main focus point. All three of them are storage buildings. When the European missionaries arrived, they reported that the most elaborate sanctuary buildings in the village were not the chief's houses; they were the store rooms. There's an entirely alternative approach to property compared to the European approach. The rua goes down underground so it keeps the kumara's cool and dry. The patika and the whatarangi are raised up, to give security from feeding kids and rats.
+
+Storage house design varied depending on the tribe... some reports would say a whatarangi was 10 metres in the air with one single pole, and it was for dead people; they would put skeletons up there. 
+If you look at the bottom panel of teh largest structure  - that's a replica of a piece of wood that was found buried in a garden in Chartwell in the 1970s; this original piece of wood now belongs to the Waikato museum. So every effort has been made in this garden to make sure that the carvings are accurate, to pre-European carving style. Because of course, like any art form, there's changes in style and changes in technique, so where practical they have used traditional stone tools and traditional patterns 
+
+  </text>
+<tts>
+
+You can see three structures here. They are really interesting and the main focus point. All three of them are storage buildings. When the European missionaries arrived, they reported that the most elaborate sanctuary buildings in the village were not the chief's houses; they were the store rooms. There's an entirely alternative approach to property compared to the European approach. The rua goes down underground so it keeps the koomera's cool and dry. The pateeke and the fataran-kee are raised up, to give security from feeding kids and rats.
+
+Storage house design varied depending on the tribe... some reports would say a fataran-kee was ten metres in the air with one single pole, and it was for dead people; they would put skeletons up there. 
+If you look at the bottom panel of the largest structure  - that's a replica of a piece of wood that was found buried in a garden in Chartwell in the 1970s; this original piece of wood now belongs to the Waikato museum. So every effort has been made in this garden to make sure that the carvings are accurate, to pre-European carving style. Because of course, like any art form, there's changes in style and changes in technique, so where practical they have used traditional stone tools and traditional patterns 
+
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.760980725</latitude>
+  <longitude>175.308644771</longitude>
+  <radius>0.02</radius>
+  <title>17 Ernest Road</title>
+  <text>
+    Ah our home at last.  I'll just sit down and have a nice rest ...
+  </text>
+  <audio>
+  </audio>
+</location>
+
+
+<location>
+  <latitude>-37.76091</latitude>
+  <longitude>175.309101</longitude>
+  <radius>0.02</radius>
+  <title>21 Ernest Road</title>
+  <text>
+    The home of my friends Cara and Kaden.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<!-- Univeristy of Waikato -->
+
+
+<location>
+  <latitude>-37.788972</latitude>
+  <longitude>175.317668</longitude>
+  <radius>0.03</radius>
+  <title>G-Block</title>
+  <text>
+    This building houses the departments of Computer Science, Mathematics
+    and Statistics.
+
+    Here are some Maori names:
+      kumara, tapu, Waikato, Tainui, Patika, Whatarangi, Taro, Matariki, Pirongia, Patupairehe, Marae, Pa, Hoturoa, Te Parapara, Pohutekawa, Kawhia, Pomaderris
+  </text>
+  <tts>
+    This building houses the departments of Computer Science, Mathematics
+    and Statistics.
+
+    Here are some Maori names:
+      koomera, tapoo, Waikato, Tye-newee, Pateeke, Fataran-gee, Taaro, Maataareekee, Peeron-keaa, Paatoopai-areehee, Maarai, Paa, Hotooro-aa, Tee Paaraa-paaraa, Poh-hooteekawaa, Kefeea, Poh-maadeerrees
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.78764</latitude>
+  <longitude>175.317732</longitude>
+  <radius>0.05</radius>
+  <title>The Library</title>
+  <text>
+  </text>
+  <audio>
+  </audio>
+</location>
+
+
+<!-- Empty space -->
+<!-- geo fix 175.308644771 -37.7605 -->
+
+<!-- 17 Ernest Road -->
+<!-- geo fix 175.308644771 -37.760980725 -->
+
+<!-- 21 Ernest Road -->
+<!-- geo fix 175.309101 -37.76091 -->
+
+<!-- Garden start -->
+<!-- geo fix 175.3029395 -37.80608675 -->
+
+    	
+</tour>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/assets/info.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/assets/info.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/assets/info.xml	(revision 24268)
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+	<head>
+		<title>About this software</title>
+		<meta http-equiv="Content-Style-Type" content="text/css" />
+	</head>
+	<body style="padding: 1em;">
+		<p style="text-align: center;">
+		    <img alt="tipple logo" src="file:///android_asset/tipple_eab_about.png" 
+		         style="width: 256px; height: 223px;" /><br />Version 1.0</p>
+		<p>TIPPLE is the culmination of the  
+		    <a href="http://www.greenstone.org/">Greenstone</a> and
+		    Tourist Information Provider (TIP) research projects conducted at the 
+		    <a href="http://www.cs.waikato.ac.nz">Department of Computer Science</a>
+		    at the <a href="http://www.waikato.ac.nz/">University of
+		    Waikato</a>.</p>
+		<p>All map data (c) <a href="http://www.openstreetmap.org/">OpenStreetMap</a> 
+		  contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA license</a>.</p>
+	</body>
+</html>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/default.properties
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/default.properties	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/default.properties	(revision 24268)
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-8
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/proguard.cfg
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/proguard.cfg	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/proguard.cfg	(revision 24268)
@@ -0,0 +1,36 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclasseswithmembernames class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembernames class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/layout/filebrowser.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/layout/filebrowser.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/layout/filebrowser.xml	(revision 24268)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+	<GridView android:id="@+id/fileBrowserView"
+		android:layout_width="fill_parent" android:layout_height="fill_parent"
+		android:fadingEdge="vertical" android:fadingEdgeLength="30dip"
+		android:columnWidth="100dip" android:numColumns="auto_fit" />
+</merge>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/layout/main.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/layout/main.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/layout/main.xml	(revision 24268)
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView  
+    android:layout_width="fill_parent" 
+    android:layout_height="wrap_content" 
+    android:text="@string/hello"
+    />
+</LinearLayout>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/menu/options_menu.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/menu/options_menu.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/menu/options_menu.xml	(revision 24268)
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:id="@+id/menu_info" android:title="@string/menu_info"
+		android:icon="@drawable/ic_menu_info_details">
+	</item>
+	
+	<item android:id="@+id/menu_preferences" android:title="@string/menu_preferences"
+		android:icon="@drawable/ic_menu_preferences" />
+			
+	<item android:id="@+id/menu_logfile" android:title="@string/menu_logfile"
+		android:icon="@drawable/ic_menu_archive" />
+		
+</menu>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/values/arrays.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/values/arrays.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/values/arrays.xml	(revision 24268)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<string-array name="preferences_text_audio_mode_entries">
+		<item>Text only</item>
+		<item>Text with audio</item>
+		<item>Audio only</item>
+		<item>Audio with text</item>
+	</string-array>
+
+	<string-array name="preferences_text_audio_mode_entry_values">
+		<item>TEXT_ONLY</item>
+		<item>TEXT_PLUS_AUDIO</item>
+		<item>AUDIO_ONLY</item>
+		<item>AUDIO_PLUS_TEXT</item>
+	</string-array>
+
+	<string name="preferences_text_audio_mode_default">AUDIO_PLUS_TEXT</string>
+</resources>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/values/strings.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/values/strings.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/values/strings.xml	(revision 24268)
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Hello World, TippleActivity!</string>
+    <string name="app_name">Tipple</string>
+    
+    <!--  General labels -->
+    <string name="cancel">Cancel</string>
+	<string name="error">Error</string>
+	<string name="ok">OK</string>
+    <string name="latitude">Latitude</string>
+	<string name="longitude">Longitude</string>
+	
+	<!--  Values used in OptionsMenu -->
+    <string name="menu_info">Info</string>
+    <string name="menu_preferences">Preferences</string>
+    <string name="menu_logfile">Log file</string>
+    
+	<string name="log_file_invalid">
+	    The selected file is not a valid log file. 
+		Please select another file.
+	</string>
+	<string name="log_file_select">Please select a log file.</string>
+	
+	
+    <string name="logging_error_continue">Continue anyway</string>
+    <string name="logging_error_stop">Stop Tipple</string>
+    <string name="logging_ok_enable">Enable</string>
+    <string name="logging_ok_disable">Disable</string>
+     
+    <string name="preferences_map">Map settings</string>
+    <string name="preferences_show_scale_bar">Map scale bar</string>
+	<string name="preferences_show_scale_bar_desc">Show the scale of the map</string>
+	<string name="preferences_centre_on_loc">Centre on GPS location</string>
+	<string name="preferences_centre_on_loc_desc">Automatically centres the map based on your GPS location</string>
+	<string name="preferences_show_sights">Show sights</string>
+	<string name="preferences_show_sights_desc">Show the landmarks that Tipple has information about</string>
+	<string name="preferences_show_user_trail">Show your trail</string>
+	<string name="preferences_show_user_trail_desc">Display the recorded route walked for a designated log file</string>
+	
+	<string name="preferences_mode">Text/Audio settings</string>
+	<string name="preferences_text_audio_mode">Text and/or Audio</string>
+	<string name="preferences_text_audio_mode_desc">Control the ability to display text and play audio</string>
+	
+	
+	
+</resources>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/xml/preferences.xml
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/xml/preferences.xml	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/res/xml/preferences.xml	(revision 24268)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+	android:title="@string/menu_preferences">
+	<PreferenceCategory android:title="@string/preferences_map">
+		<CheckBoxPreference android:title="@string/preferences_show_scale_bar"
+			android:summary="@string/preferences_show_scale_bar_desc"
+			android:key="showScaleBar" />
+		<CheckBoxPreference android:title="@string/preferences_centre_on_loc"
+			android:summary="@string/preferences_centre_on_loc_desc"
+			android:key="centreOnGPSLoc" />
+		<CheckBoxPreference android:title="@string/preferences_show_sights"
+			android:summary="@string/preferences_show_sights_desc"
+			android:key="showSights" />		
+		<CheckBoxPreference android:title="@string/preferences_show_user_trail"
+			android:summary="@string/preferences_show_user_trail_desc"
+			android:key="showUserTrail" />			
+	
+	</PreferenceCategory>
+	
+    <PreferenceCategory android:title="@string/preferences_mode">
+    	<ListPreference android:title="@string/preferences_text_audio_mode"
+			android:summary="@string/preferences_text_audio_mode_desc"
+			android:entries="@array/preferences_text_audio_mode_entries"
+			android:entryValues="@array/preferences_text_audio_mode_entry_values"
+			android:key="textAudioMode" android:defaultValue="@string/preferences_text_audio_mode_default" />
+    </PreferenceCategory>
+
+</PreferenceScreen>
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/AudioPlayerView.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/AudioPlayerView.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/AudioPlayerView.java	(revision 24268)
@@ -0,0 +1,621 @@
+package org.greenstone.android.tipple;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class AudioPlayerView extends LinearLayout 
+{
+	TextView text_view_;
+	
+	protected Button buttonPlayPause_;
+	protected Button buttonNext_;
+	protected Button buttonShowHideText_;
+	
+	protected String stopLabel_;
+	
+	protected boolean hasCompletionHandler_;
+	
+	protected TippleActivity.TextAudioModeEnum current_text_audio_mode_;
+	
+	protected List<MediaPlayer> audioPlayerQueue_;
+	protected List<String>		idPlayerQueue_;
+	protected List<String>      textPlayerQueue_;
+	protected List<Boolean>     visitedPlayerQueue_;
+	protected int absolute_head_pos_;
+	protected int absolute_tail_pos_;
+	
+	  
+	AudioPlayerView(Context context, TextView text_view)
+	{
+		super(context);
+		
+		text_view_ = text_view;
+		
+		audioPlayerQueue_    = new LinkedList<MediaPlayer>();
+		idPlayerQueue_     = new LinkedList<String>();
+		textPlayerQueue_     = new LinkedList<String>();
+		visitedPlayerQueue_  = new LinkedList<Boolean>();
+		
+		absolute_head_pos_ = 0;
+		absolute_tail_pos_ = -1;
+		
+	    // media buttons - play/pause, and next song/audio as horizontal layout, 50% each
+		
+		stopLabel_ = "Stop";
+		
+		LayoutParams layout_params 
+	    	= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+	    									ViewGroup.LayoutParams.MATCH_PARENT,1);
+		
+	    buttonPlayPause_ = new Button(context);
+	    buttonPlayPause_.setLayoutParams(layout_params);
+	    buttonPlayPause_.setText("Play");
+	    buttonPlayPause_.setEnabled(false);
+	    
+	    buttonNext_ = new Button(context);
+	    buttonNext_.setLayoutParams(layout_params);
+	    buttonNext_.setText(stopLabel_);
+	    buttonNext_.setEnabled(false);
+	
+	    hasCompletionHandler_ = false;
+	    
+	    
+	    buttonPlayPause_.setOnClickListener(new View.OnClickListener() {
+	        public void onClick(View v) {
+	        	
+	        	if (isPlaying()) {
+	        		// Change to pause
+	        		
+	        		TippleActivity.log.optMessage("Manual Pause Audio", textPlayerQueue_.get(0));
+	    			
+	        		pauseAudio();
+	        	}
+	        	else {
+	        		// Must be paused => resume playing
+	        		TippleActivity.log.optMessage("Manual Play/Resume Audio", textPlayerQueue_.get(0));
+	    			
+	        		playResumeAudio();
+	        	}
+	        }
+	    });
+	    
+	    buttonNext_.setOnClickListener(new View.OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				TippleActivity.log.optMessage("Manual Next Audio Location");
+				nextAudioLocation();
+			}
+		});
+	    
+	    // Optional show text button
+	    
+	    buttonShowHideText_ = new Button(context);
+ 	    buttonShowHideText_.setLayoutParams(layout_params);
+ 	    buttonShowHideText_.setText("Show Text");
+ 	    buttonShowHideText_.setEnabled(false);
+ 	    
+ 	    buttonShowHideText_.setOnClickListener(new View.OnClickListener() {
+ 	        public void onClick(View v) {
+ 	        	TippleActivity.log.optMessage("Manual Show/Hide Text");
+ 	        	
+ 	        	if (text_view_.getVisibility()==View.INVISIBLE) {
+ 	        		TippleActivity.log.optMessage("Manual Show Text");
+	        		text_view_.setVisibility(View.VISIBLE);
+	        		buttonShowHideText_.setText("Hide Text");
+	        	}
+	        	else {
+	        		TippleActivity.log.optMessage("Manual Hide Text");
+	        		text_view_.setVisibility(View.INVISIBLE);
+	        		buttonShowHideText_.setText("Show Text");
+	        	}
+ 	        }
+ 	    });
+    
+	    	
+	  
+	    
+	    // Horozontal layout is:
+	    // Play | OptText | Next
+	    
+	    addView(buttonPlayPause_);
+	    
+	    /*
+	    current_text_audio_mode_ = TippleActivity.textAudioMode;
+	    if (current_text_audio_mode_ == TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) {
+	    	addView(buttonShowHideText_);
+	    }
+	  */
+	    addView(buttonShowHideText_);
+	    
+	    addView(buttonNext_);
+	}
+	
+	protected boolean audioViewIsActive()
+	{
+		return ((current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_ONLY)
+				|| (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT)
+				|| (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO));
+		
+	}
+	
+	protected boolean textViewIsActive()
+	{
+		return ((current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_ONLY)
+				|| (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO)
+				|| (current_text_audio_mode_==TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT));
+		
+	}
+	
+	public void refreshView() 
+	{
+		if (current_text_audio_mode_ != TippleActivity.textAudioMode) {
+			// There has been a mode change
+			
+			 if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.AUDIO_PLUS_TEXT) {
+				 // ensure both play/pause and show/hide removed, then add in order needed
+				 
+				 if (this.indexOfChild(buttonPlayPause_)>=0) {
+					 this.removeView(buttonPlayPause_);
+				 }
+				 if (this.indexOfChild(buttonShowHideText_)>=0) {
+					 this.removeView(buttonShowHideText_);
+				 }
+				 this.addView(buttonPlayPause_,0);
+				 this.addView(buttonShowHideText_,1);
+				 
+				 stopLabel_ = "Stop";
+			 }
+			 
+			 else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.AUDIO_ONLY) {
+				 // ensure show/hide is gone, and only add play/pause if needed
+				 if (this.indexOfChild(buttonShowHideText_)>=0) {
+					 // Don't want to see a show/hide text button
+					 this.removeView(buttonShowHideText_);
+				 }
+				 if (this.indexOfChild(buttonPlayPause_)<0) {
+					 this.addView(buttonPlayPause_);
+				 }
+				 stopLabel_ = "Stop";
+			 }
+			 
+			 else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.TEXT_PLUS_AUDIO) {
+				 // ensure both play/pause and show/hide removed, then add in order needed
+				 if (this.indexOfChild(buttonPlayPause_)>=0) {
+					 this.removeView(buttonPlayPause_);
+				 }
+				 if (this.indexOfChild(buttonShowHideText_)>=0) {
+					 this.removeView(buttonShowHideText_);
+				 }
+				 
+				 this.addView(buttonShowHideText_,0);
+				 this.addView(buttonPlayPause_,1);
+				 
+				 stopLabel_ = "Stop";
+			 }
+			 
+			 else if (TippleActivity.textAudioMode == TippleActivity.TextAudioModeEnum.TEXT_ONLY) {
+				 // ensure play/pause is gone, and only add show/hide if needed
+				 
+				 if (this.indexOfChild(buttonPlayPause_)>=0) {
+					 // Don't want to see a play button
+					 this.removeView(buttonPlayPause_);
+				 }
+				 
+				 if (this.indexOfChild(buttonShowHideText_)<0) {
+					 this.addView(buttonShowHideText_,0);
+				 }
+				 stopLabel_ = "End";
+			 }
+			 
+			 current_text_audio_mode_ = TippleActivity.textAudioMode;
+		}
+	
+	}
+	synchronized public boolean audioQueueEmpty()
+	{
+		return (audioPlayerQueue_.size()==0);
+	}
+	
+	synchronized public boolean outstandingAudioPending()
+	{
+		return (audioPlayerQueue_.size()>1);
+	}
+	
+	synchronized public int audioQueueLength()
+	{
+		return audioPlayerQueue_.size();
+	}
+	
+	synchronized public boolean addAudio(String new_id) 
+	{
+		// Assume audio should be added, unless an existing match is found
+		boolean add_audio = true;
+		
+		int rev_pos = audioPlayerQueue_.size()-1;
+		for (int i=0; i<3; i++) {
+			if (rev_pos<0) {
+				break;
+			}
+			
+			String id = idPlayerQueue_.get(rev_pos);
+			
+			if (new_id.equals(id)) {
+				add_audio=false;
+			}
+			
+			rev_pos--;
+		}
+		
+		return add_audio;
+	}
+	synchronized public void queueAudio(String audio_filename, String id, String text, 
+										boolean previously_visited)
+	{
+		MediaPlayer mp = new MediaPlayer();
+		try {
+			
+			mp.setDataSource(audio_filename);
+			mp.prepare();
+			audioPlayerQueue_.add(mp);
+			idPlayerQueue_.add(id);
+			textPlayerQueue_.add(text);
+			visitedPlayerQueue_.add(previously_visited);
+			
+			absolute_tail_pos_++;
+			
+			int audio_queue_len = audioPlayerQueue_.size();
+			int mod_head_pos = absolute_head_pos_ % 3;
+			
+			if (audio_queue_len==1) {
+				// Nothing to do, not enough data to allow the user to play anything
+				return;
+			}
+			else if (audio_queue_len==2) {
+				
+				// mod_head_pos==0 => title followed by text => ready to play
+				// mod_head_pos==1 => text followed by beep => "stop"
+				// mod_head_pos==2 => beep followed title => "skip to next"
+				
+				if (mod_head_pos==0) {
+					// just queued up a title and text (at the head of the queue) 
+					// => make play active if not already so 
+					if (!buttonPlayPause_.isEnabled()) {
+						//buttonPlayPause_.setText("Play");
+						buttonPlayPause_.setEnabled(true);
+					}
+					if (!buttonShowHideText_.isEnabled()) {
+						buttonShowHideText_.setEnabled(true);
+						
+					}
+					buttonNext_.setText(stopLabel_);
+					buttonNext_.setEnabled(true);
+				}
+				else if (mod_head_pos==1) {
+					
+					buttonNext_.setText(stopLabel_);
+					buttonNext_.setEnabled(true);
+				}
+				else {
+					// mod_head_pos==2
+					buttonNext_.setText("Skip to Next (1)");
+					buttonNext_.setEnabled(true);	
+				}
+			}
+			else {
+				// length >= 3
+				
+				// mod_head_pos==0 => title at head, spans loc if len >3
+				// mod_head_pos==1 => text at head, guaranteed to span loc => "skip to next"
+				// mod_head_pos==2 => beep at head, guaranteed to span loc => "skip to next"
+				
+				if ((mod_head_pos==0) && (audio_queue_len==3)) {
+					// stop 
+					buttonNext_.setText(stopLabel_);
+					buttonNext_.setEnabled(true);
+				}
+				else {
+					// for everything else, the queue spans to another location
+			
+					
+					int relative_adjustment_pos = mod_head_pos -1;
+					int num_remaining_locations = (audio_queue_len+relative_adjustment_pos)/3;
+
+					buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")");
+					buttonNext_.setEnabled(true);					
+				}
+			}
+		}
+		catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	synchronized public void queueAudio(String audio_filename, String id, String text)
+	{
+		queueAudio(audio_filename,id,text,false);
+	}
+	
+	synchronized public void removeAudioTrack()
+	{
+		if (audioPlayerQueue_.size()>0) {
+			
+			TippleActivity.log.optMessage("Remove Audio Track","Queue size was: " + audioPlayerQueue_.size());
+			
+			MediaPlayer mp = audioPlayerQueue_.remove(0);
+			idPlayerQueue_.remove(0);
+			textPlayerQueue_.remove(0);
+			visitedPlayerQueue_.remove(0);
+			
+			absolute_head_pos_++;
+			mp.stop();
+			mp.release();				
+			
+			hasCompletionHandler_ = false;
+		}
+		else {
+			System.err.println("Error: No audio in queue to remove");
+		}
+	}
+	
+	synchronized public void removeAudioLocation()
+	{
+		int audio_queue_len = audioPlayerQueue_.size();
+		
+		if (audio_queue_len>0) {
+	
+			TippleActivity.log.optMessage("Remove Audio Location","Queue size was: " + audioPlayerQueue_.size());
+			
+			int mod_head_pos = absolute_head_pos_%3;
+			int need_to_skip = 3-mod_head_pos;
+				
+			// remove up to the next absolute multiple of 3
+			for (int i=0; i<need_to_skip; i++) {
+				MediaPlayer mp = audioPlayerQueue_.remove(0);
+				idPlayerQueue_.remove(0);
+				textPlayerQueue_.remove(0);
+				visitedPlayerQueue_.remove(0);
+				absolute_head_pos_++;
+				
+				mp.stop();
+				mp.release();
+				if (audioPlayerQueue_.size()==0) {
+					// happens if there is no beep on the last location
+					break;
+				}
+			}
+			
+			hasCompletionHandler_ = false;
+		}
+		else {
+			System.err.println("Error: No audio in queue to remove");
+		}
+	}
+	
+	
+	synchronized public void onAudioCompletion(MediaPlayer mp)
+	{
+		mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+			
+			@Override
+			public void onCompletion(MediaPlayer mp) {
+				nextAudioTrack();	
+			}
+		});
+	}
+
+	synchronized public void onVisitedAudioCompletion(MediaPlayer mp)
+	{
+		mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+			
+			@Override
+			public void onCompletion(MediaPlayer mp) {
+				playResumeAudioInternal();
+			}
+		});
+	}
+
+	public boolean updateTextViewIfHeadIsNewLocation() 
+	{
+		boolean updated = false;
+		
+		if ((absolute_head_pos_%3)==0) {
+			// start of a new location
+			String text_desc = textPlayerQueue_.get(0);
+			if (visitedPlayerQueue_.get(0)) {
+				text_desc += "\n(Previously Visited)";
+			}
+			text_desc += "\n------";
+			
+			if (textPlayerQueue_.size()>1) {
+				text_desc += "\n" + textPlayerQueue_.get(1);
+			}
+			
+			text_view_.setText(text_desc);
+			updated = true;
+		}
+		
+		return updated;
+	}
+	
+	
+	synchronized public void playResumeAudio()
+	{
+		if (audioPlayerQueue_.size()>0) {
+			
+			if ((audioViewIsActive()) && (visitedPlayerQueue_.get(0))) {
+				// Play (synchronously) the "previously visited" message before moving on to
+				// describing the location itself
+				MediaPlayer mp = new MediaPlayer();
+				
+				try {
+					mp.setDataSource(TipLocationManager.prevVisitedFilename);
+					mp.prepare();
+					onVisitedAudioCompletion(mp);
+					mp.start();
+				}
+				catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+			else {
+				// go straight on and play the queued audio
+				playResumeAudioInternal();
+			}
+		}
+		else {
+			System.err.println("Error: No audio in queue to start playing");
+		
+		}
+		
+	}
+	
+	
+	synchronized public void playResumeAudioInternal()
+	{
+		if (audioPlayerQueue_.size()>0) {
+			TippleActivity.log.optMessage("Play/Resume Audio","Queue size was: " + audioPlayerQueue_.size());
+			
+			MediaPlayer mp = audioPlayerQueue_.get(0);
+
+			if (!hasCompletionHandler_) {
+				
+				updateTextViewIfHeadIsNewLocation();
+				
+				onAudioCompletion(mp);
+				hasCompletionHandler_ = true;
+			}
+			
+			if (audioViewIsActive() || ((absolute_head_pos_%3)==2)) {
+				// allow it to play a "ding" even in text only mode
+				mp.start();
+			}
+			buttonPlayPause_.setText("Pause");      // next operation 
+		}
+		else {
+			System.err.println("Error: No audio in queue to start playing");
+		}
+		
+	}
+	
+	synchronized public void pauseAudio()
+	{
+		if (audioPlayerQueue_.size()>0) {
+			MediaPlayer mp = audioPlayerQueue_.get(0);
+			mp.pause();
+			buttonPlayPause_.setText("Play/Resume");      // next operation 
+		}
+		else {
+			System.err.println("Error: No audio in queue to pause");
+		}
+	}
+	
+	synchronized public void nextAudioLocation()
+	{
+		removeAudioLocation(); // stops it if currently playing
+
+		int remaining_audio_len = audioQueueLength();				
+		if (remaining_audio_len>0) {
+			playResumeAudio();
+			
+			int mod_head_pos = absolute_head_pos_ % 3;
+			/*
+			int relative_adjustment_pos = 1 - mod_head_pos;
+			int num_remaining_locations = (remaining_audio_len+relative_adjustment_pos)/3;
+			
+			if (num_remaining_locations>=1) {
+				
+				buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")");
+				buttonNext_.setEnabled(true);
+			}
+			*/
+			
+			int num_remaining_locations = (remaining_audio_len+mod_head_pos)/3;
+			
+			if (num_remaining_locations>1) {
+				
+				buttonNext_.setText("Skip to Next (" + (num_remaining_locations-1) + ")");
+				buttonNext_.setEnabled(true);
+			}
+			
+			else {
+				buttonNext_.setText(stopLabel_);
+				buttonNext_.setEnabled(true);
+				buttonPlayPause_.setText("Play");
+				buttonPlayPause_.setEnabled(true);
+				buttonShowHideText_.setText("Show Text");
+				buttonShowHideText_.setEnabled(true);
+			}
+		}
+		else {
+			buttonNext_.setText(stopLabel_);
+			buttonNext_.setEnabled(false);
+			buttonPlayPause_.setText("Play");
+			buttonPlayPause_.setEnabled(false);
+			
+			if (text_view_.getVisibility() == View.VISIBLE) {
+				TippleActivity.log.optMessage("Audio Hide Text");
+				text_view_.setVisibility(View.INVISIBLE);
+			}
+			buttonShowHideText_.setText("Show Text");
+    		buttonShowHideText_.setEnabled(false);
+		}
+	} 
+	
+	synchronized public void nextAudioTrack()
+	{
+		removeAudioTrack(); // stops it if currently playing
+
+		int remaining_audio_len = audioQueueLength();				
+		if (remaining_audio_len>0) {
+			playResumeAudio();
+			
+			int num_remaining_locations = ((remaining_audio_len-1)/3);
+			
+			if (num_remaining_locations>=1) {
+				
+				buttonNext_.setText("Skip to Next (" + num_remaining_locations + ")");
+				buttonNext_.setEnabled(true);
+			}
+			else {
+				buttonNext_.setText(stopLabel_);
+				buttonNext_.setEnabled(true);
+				//buttonPlayPause_.setText("Play");
+				buttonPlayPause_.setEnabled(true);
+				buttonShowHideText_.setEnabled(true);
+			}
+		}
+		else {
+			buttonNext_.setText(stopLabel_);
+			buttonNext_.setEnabled(false);
+			buttonPlayPause_.setText("Play");
+			buttonPlayPause_.setEnabled(false);
+			
+			if (text_view_.getVisibility() == View.VISIBLE) {
+				TippleActivity.log.optMessage("Audio Hide Text");
+				text_view_.setVisibility(View.INVISIBLE);
+			}
+    		buttonShowHideText_.setText("Show Text");
+    		buttonShowHideText_.setEnabled(false);
+    		
+    		
+		}
+	} 
+	
+	
+	synchronized public boolean isPlaying()
+	{
+		boolean is_playing = false;
+		if (audioPlayerQueue_.size()>0) {
+			is_playing = audioPlayerQueue_.get(0).isPlaying();
+		}
+		return is_playing;
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/EditPreferences.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/EditPreferences.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/EditPreferences.java	(revision 24268)
@@ -0,0 +1,50 @@
+/*
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Based on file of the same name from mapsforge.org
+ * 
+ */
+package org.greenstone.android.tipple;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+/**
+ * Activity to edit the application preferences.
+ */
+public class EditPreferences extends PreferenceActivity 
+{
+	@Override
+	protected void onCreate(Bundle savedInstanceState) 
+	{
+		System.err.println("*** EditPreferences::onCreate()");
+		super.onCreate(savedInstanceState);
+		addPreferencesFromResource(R.xml.preferences);
+	}
+
+	@Override
+	protected void onResume() 
+	{
+		System.err.println("*** EditPreferences::onResume()");
+		super.onResume();
+	}
+	
+	@Override
+	protected void onStop() 
+	{
+		System.err.println("*** EditPreferences::onStop()");
+		super.onResume();
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/InfoView.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/InfoView.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/InfoView.java	(revision 24268)
@@ -0,0 +1,43 @@
+/*
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Based on file of the same name from mapsforge.org
+ */
+package org.greenstone.android.tipple;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.webkit.WebView;
+
+/**
+ * Simple activity to display the info web page from the assets folder.
+ */
+public class InfoView extends Activity 
+{
+	@Override
+	protected void onCreate(Bundle savedInstanceState) 
+	{
+		super.onCreate(savedInstanceState);
+		WebView webView = new WebView(this);
+		webView.loadUrl("file:///android_asset/info.xml");
+		setContentView(webView);
+	}
+
+	@Override
+	protected void onResume() 
+	{
+		super.onResume();
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TipLocation.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TipLocation.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TipLocation.java	(revision 24268)
@@ -0,0 +1,99 @@
+package org.greenstone.android.tipple;
+
+import java.util.List;
+import java.util.HashMap;
+//import java.util.ArrayList;
+
+public class TipLocation 
+{
+	
+	// Text and/or Audio associated with (Latitude,Longitude) location with radius
+	
+	private double latitude_;
+	private double longitude_;
+	
+	private double radiusInKM_;
+	//private double avgRadLatLong_;
+	
+	private String title_;
+	private String text_;
+	private String tts_text_;
+	
+	
+	private String audioFilename_;
+	private List<String> audioPaths_;
+	
+	protected String id_;
+	
+	public TipLocation(HashMap<String,String> hashmap)
+	{
+		latitude_  = Double.parseDouble(hashmap.get("latitude"));
+		longitude_ = Double.parseDouble(hashmap.get("longitude"));
+		radiusInKM_ = Double.parseDouble(hashmap.get("radius"));
+		
+		title_ = hashmap.get("title");
+		text_  = hashmap.get("text");
+		tts_text_ = (hashmap.containsKey("tts")) ? hashmap.get("tts") : null;
+		
+		audioFilename_ = hashmap.get("audio");
+		
+		// Consolidate whitespace
+		title_ = title_.replaceAll("^\\s+", "");
+		title_ = title_.replaceAll("\\s+$", "");
+		title_ = title_.replaceAll("\\s+", " ");
+
+		if (text_ != null) {
+			text_ = text_.replaceAll("^\\s+", "");
+			text_ = text_.replaceAll("\\s+$", "");
+			text_ = text_.replaceAll("\\s+", " ");
+		}
+		
+		id_ = title_.replaceAll("/\\s+/", "-");
+	}
+	
+	
+	public double getLatitude() 
+	{
+		return latitude_;
+	}
+	
+	public double getLongitude() 
+	{
+		return longitude_;
+	}
+	
+	public double getRadiusKM() 
+	{
+		return radiusInKM_;
+	}
+
+	public String getText() 
+	{
+		return text_;
+	}
+	
+	public String getTTSText()
+	{
+		return (tts_text_ != null) ? tts_text_ : text_;
+	}
+	public String getTitle() 
+	{
+		return title_;
+	}
+	
+	public String getID()
+	{
+		return id_;
+	}
+	
+	public List<String> getAudio() 
+	{
+		return audioPaths_;
+	}
+	
+	public String getAudioFilename()
+	{
+		return audioFilename_;
+	}
+	
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TipLocationManager.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TipLocationManager.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TipLocationManager.java	(revision 24268)
@@ -0,0 +1,444 @@
+package org.greenstone.android.tipple;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.greenstone.android.tipple.R;
+import org.mapsforge.android.maps.ArrayItemizedTextAudioOverlay;
+import org.mapsforge.android.maps.CirclesOverlay;
+import org.mapsforge.android.maps.GeoPoint;
+import org.mapsforge.android.maps.MapView;
+import org.mapsforge.android.maps.Overlay;
+import org.mapsforge.android.maps.OverlayItem;
+import org.mapsforge.android.maps.OverlayTextAudioItem;
+import org.mapsforge.android.maps.RouteOverlayResetable;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.DashPathEffect;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
+import android.util.Log;
+
+public class TipLocationManager 
+{
+	Activity activity_;
+	AudioPlayerView audio_player_view_;
+	TextToSpeech tts_;
+	
+	public static CirclesOverlay circlesOverlay=null; 
+	public static ArrayItemizedTextAudioOverlay itemizedMarkerOverlay;
+	public static ArrayList<OverlayItem> itemizedMarkerOverlayArrayList;
+	public static ArrayList<GeoPoint> circlesOverlayGeoPoints;
+	public static ArrayList<Integer> circlesOverlayRadii;
+	
+	public static RouteOverlayResetable routeOverlay=null;
+	public static ArrayItemizedTextAudioOverlay routeMarkerOverlay;
+	public static GeoPoint[] routeOverlayArray;
+	
+	public static ArrayList<OverlayItem> routeMarkerOverlayArrayList;
+	
+	public static String prevVisitedFilename;
+	
+	TipLocationManager(Activity activity, AudioPlayerView audio_player_view, 
+						 TextToSpeech tts)
+	{
+		activity_ = activity;
+		audio_player_view_ = audio_player_view;
+		tts_ = tts;
+	
+	
+		
+	}
+	
+	protected void createLogRouteLocationsOverlay(MapView mapView,ArrayList<TipLocation> locations)
+	{
+		Paint fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+		fillPaint.setStyle(Paint.Style.STROKE);
+		fillPaint.setColor(Color.BLUE);
+		fillPaint.setAlpha(160);
+		fillPaint.setStrokeWidth(7);
+		fillPaint.setStrokeCap(Paint.Cap.BUTT);
+		fillPaint.setStrokeJoin(Paint.Join.ROUND);
+		fillPaint.setPathEffect(new DashPathEffect(new float[] { 20, 20 }, 0));
+
+		Paint outlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+		outlinePaint.setStyle(Paint.Style.STROKE);
+		outlinePaint.setColor(Color.BLUE);
+		outlinePaint.setAlpha(96);
+		outlinePaint.setStrokeWidth(7);
+		outlinePaint.setStrokeCap(Paint.Cap.BUTT);
+		outlinePaint.setStrokeJoin(Paint.Join.ROUND);
+	
+		Drawable defaultMarker = activity_.getResources().getDrawable(R.drawable.routemarker);
+		
+		if (routeOverlay==null) {
+			routeOverlay = new RouteOverlayResetable(fillPaint, outlinePaint);
+			routeMarkerOverlay = new ArrayItemizedTextAudioOverlay(defaultMarker,activity_,tts_);
+			routeMarkerOverlayArrayList = new ArrayList<OverlayItem>();
+		
+		}
+		// This one needs to be done again, incase it is of a different size
+		routeOverlayArray = new GeoPoint[locations.size()];
+		
+		for (int i=0;  i<locations.size(); i++) {
+			TipLocation location = locations.get(i);
+			double latitude  = location.getLatitude();
+			double longitude = location.getLongitude();
+			String title     = location.getTitle();
+			String text      = location.getText();
+			String tts_text  = location.getTTSText();
+			GeoPoint geoPoint = new GeoPoint(latitude,longitude);
+				
+			routeOverlayArray[i] = geoPoint;
+			
+			if (title.startsWith("Manual")) {
+				// ItemizedOverlay => display marker with text for info pop-up window
+				OverlayTextAudioItem overlayItem = new OverlayTextAudioItem(geoPoint, title, text, tts_text);
+				
+				//routeMarkerOverlay.addOverlay(overlayItem);
+				routeMarkerOverlayArrayList.add(overlayItem);
+			}
+		}
+		
+		//routeOverlay.setRouteData(routeOverlayArray);
+		
+		// Only add in if not already present
+		List<Overlay> overlays = mapView.getOverlays();
+		if (!overlays.contains(routeOverlay)) {
+			overlays.add(routeOverlay);
+		}
+		if (!overlays.contains(routeMarkerOverlay)) {
+			overlays.add(routeMarkerOverlay);
+		}
+	}
+	
+	protected void addLogRouteLocations(MapView mapView)
+	{	
+		for (int i=0; i<routeMarkerOverlayArrayList.size(); i++) {
+			
+			OverlayItem overlay_item = routeMarkerOverlayArrayList.get(i);
+			routeMarkerOverlay.addOverlay(overlay_item);
+		}
+	
+		routeOverlay.setRouteData(routeOverlayArray);
+		
+		mapView.invalidate();
+		
+	}
+	
+	protected void removeLogRouteLocations(MapView mapView)
+	{		
+		for (int i=0; i<routeMarkerOverlayArrayList.size(); i++) {
+			
+			OverlayItem overlay_item = routeMarkerOverlayArrayList.get(i);
+			routeMarkerOverlay.removeOverlay(overlay_item);
+			
+		}
+		
+		routeOverlay.reset();
+		
+		mapView.invalidate();
+	}
+	
+	protected void createTourLocationsOverlay(MapView mapView, ArrayList<TipLocation> locations)
+	{
+		Paint circleFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+		circleFillPaint.setStyle(Paint.Style.FILL);
+		circleFillPaint.setColor(Color.BLUE);
+		circleFillPaint.setAlpha(64);
+
+		Paint circleOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+		circleOutlinePaint.setStyle(Paint.Style.STROKE);
+		circleOutlinePaint.setColor(Color.BLUE);
+		circleOutlinePaint.setAlpha(128);
+		circleOutlinePaint.setStrokeWidth(3);
+	
+		Drawable defaultMarker = activity_.getResources().getDrawable(R.drawable.marker);
+		
+		if (circlesOverlay==null) {
+			circlesOverlay          = new CirclesOverlay(circleFillPaint, circleOutlinePaint);
+			circlesOverlayGeoPoints = new ArrayList<GeoPoint>();
+			circlesOverlayRadii     = new ArrayList<Integer>();
+		
+			itemizedMarkerOverlay = new ArrayItemizedTextAudioOverlay(defaultMarker,activity_,tts_);
+			itemizedMarkerOverlayArrayList = new ArrayList<OverlayItem>();
+		}
+		
+		for (int i=0;  i<locations.size(); i++) {
+			TipLocation location = locations.get(i);
+			double latitude  = location.getLatitude();
+			double longitude = location.getLongitude();
+			int radius       = (int)Math.round(1000.0 * location.getRadiusKM());
+			String title     = location.getTitle();
+			String text      = location.getText();
+			String tts_text  = location.getTTSText();
+			GeoPoint geoPoint = new GeoPoint(latitude,longitude);
+				
+			// CirclesOverlay => Highlight location with a circle showing catchment radius
+			//circlesOverlay.addCircleData(geoPoint, radius);
+			circlesOverlayGeoPoints.add(geoPoint);
+			circlesOverlayRadii.add(radius);
+			
+			// ItemizedOverlay => display marker with text for info pop-up window
+			OverlayTextAudioItem overlayItem = new OverlayTextAudioItem(geoPoint, title, text, tts_text);
+			 
+			//itemizedMarkerOverlay.addOverlay(overlayItem);
+			itemizedMarkerOverlayArrayList.add(overlayItem);
+		}
+		
+		// Only add in if not already present
+		List<Overlay> overlays = mapView.getOverlays();
+		if (!overlays.contains(itemizedMarkerOverlay)) {
+			overlays.add(itemizedMarkerOverlay);
+		}
+		if (!overlays.contains(circlesOverlay)) {
+			overlays.add(circlesOverlay);
+		}
+	}
+	
+	
+	protected void addTourLocations(MapView mapView)
+	{
+		for (int i=0; i<itemizedMarkerOverlayArrayList.size(); i++) {
+			
+			OverlayItem overlay_item = itemizedMarkerOverlayArrayList.get(i);
+			itemizedMarkerOverlay.addOverlay(overlay_item);
+			
+			GeoPoint geo_point = circlesOverlayGeoPoints.get(i);
+			int radius = circlesOverlayRadii.get(i);
+			circlesOverlay.addCircleData(geo_point, radius);
+		}
+
+		mapView.invalidate();
+	}
+	
+	protected void removeTourLocations(MapView mapView)
+	{	
+		for (int i=0; i<itemizedMarkerOverlayArrayList.size(); i++) {
+			
+			OverlayItem overlay_item = itemizedMarkerOverlayArrayList.get(i);
+			itemizedMarkerOverlay.removeOverlay(overlay_item);
+		}
+		
+		circlesOverlay.reset();
+		
+		mapView.invalidate();
+		
+	}
+	
+	protected ArrayList<HashMap<String,String>> parseXMLToHashMapArrayList(String xml_filename,String lock_on_element)
+	{
+		ArrayList<HashMap<String,String>> hashmap_list = null;
+		
+		try {
+			
+			// set up XML parser
+			SAXParserFactory spf = SAXParserFactory.newInstance();
+			SAXParser sp = spf.newSAXParser();
+			XMLReader xr = sp.getXMLReader();
+
+			// Create handler that maps XML Tags within <Location> to a hashmap 
+			XMLToHashmapHandler xmlTourHandler = new XMLToHashmapHandler(lock_on_element);
+			xr.setContentHandler(xmlTourHandler);
+			
+			File tour_file = new File(xml_filename);
+			FileInputStream fis = new FileInputStream(tour_file);
+			
+			InputSource is = new InputSource(fis);
+			xr.parse(is);
+			
+			// get the HashMap List that results from parsing the file
+			hashmap_list = xmlTourHandler.getHashmapList();
+			
+			
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+		return hashmap_list;
+	}
+	public ArrayList<TipLocation> loadTour(String tour_filename)
+	{
+		ArrayList<HashMap<String,String>> location_hashmap_list 
+			= parseXMLToHashMapArrayList(tour_filename,"Location");
+
+		if (location_hashmap_list != null) {
+			Log.d("TippleActivity::loadTour()", "XML Tour File Parsed");
+		}
+		
+		ArrayList<TipLocation> locations = new ArrayList<TipLocation>();
+		
+		for (int i=0;  i<location_hashmap_list.size(); i++) {
+			HashMap<String,String> location_hashmap = location_hashmap_list.get(i);
+			
+			TipLocation location = new TipLocation(location_hashmap);
+			locations.add(location);
+		}
+		
+		return locations;
+	}
+	
+	protected ArrayList<TipLocation> loadUserTrail(String log_filename)
+	{
+		ArrayList<HashMap<String,String>> logentry_hashmap_list 
+			= parseXMLToHashMapArrayList(log_filename,"LogEntry");
+
+		ArrayList<TipLocation> locations = new ArrayList<TipLocation>();
+		
+		if (logentry_hashmap_list != null) {
+			Log.d("TippleActivity::loadUserTrail()", "XML User Activity Log File Parsed");
+			
+
+			for (int i=0;  i<logentry_hashmap_list.size(); i++) {
+				HashMap<String,String> logentry_hashmap = logentry_hashmap_list.get(i);
+				
+				HashMap<String,String> location_hashmap = new HashMap<String,String>();
+				location_hashmap.put("longitude",logentry_hashmap.get("longitude"));
+				location_hashmap.put("latitude",logentry_hashmap.get("latitude"));
+				location_hashmap.put("radius",logentry_hashmap.get("accuracy"));
+				location_hashmap.put("title",logentry_hashmap.get("action"));
+				location_hashmap.put("text",logentry_hashmap.get("message"));
+				
+				TipLocation location = new TipLocation(location_hashmap);
+				locations.add(location);
+			}
+		}
+		
+		return locations;
+	}
+	
+	
+	protected String SyncSynthesizeToFile(String text, String id, String root_utterance)
+	{
+		String audio_dir = TippleActivity.audioDirectory;
+		File tts_file = new File(audio_dir, id+"-"+root_utterance+"-TTS.wav");
+    	String tts_filename = tts_file.getAbsolutePath(); 	
+    	
+    	if (tts_file.exists()) {
+    		// already synthesized this
+    		return tts_filename;
+    	}
+    	
+		// Rendering the supplied text of the id+utterance derived WAV file
+    	HashMap<String, String> hashRender = new HashMap<String, String>();
+    	hashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, text);
+    	
+    	String UTTERANCE = root_utterance+"-utterance";
+    	
+    	hashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, UTTERANCE);
+    	UtteranceWaitListener listener = new UtteranceWaitListener(UTTERANCE);
+    	tts_.setOnUtteranceCompletedListener(listener);
+    	
+    	 tts_.synthesizeToFile(text, hashRender, tts_filename);
+    	 
+    	try {
+    		listener.waitForComplete();
+    	} 
+    	catch (Exception e) {
+    		e.printStackTrace();
+    		return null;
+    	}
+    	
+    	return tts_filename;
+	}
+	
+	boolean genPrevVisited = true;
+	
+	public boolean textToSpeechToLocation(TipLocation tip_location, boolean visited_previously) 
+	{
+		if (genPrevVisited) {
+			prevVisitedFilename = SyncSynthesizeToFile("Previously visited.","Previously-visited","cue");
+			System.err.println("** Prev visisted file = " + prevVisitedFilename);
+			genPrevVisited = false;
+		}
+		
+		String title = tip_location.getTitle();
+		String text = tip_location.getText();
+		String id = tip_location.getID();
+		
+		String title_filename = SyncSynthesizeToFile(title,id,"title");
+		String text_filename = SyncSynthesizeToFile(text,id,"text");
+	 
+		if ((title_filename!=null) && (text_filename!=null)) {
+			String audio_dir = TippleActivity.audioDirectory;
+			File ding_file = new File(audio_dir, "DingExtended.wav");
+			String ding_filename = ding_file.getAbsolutePath();
+			
+			if (audio_player_view_.addAudio(id)) {
+				TippleActivity.log.optMessage("Queue Audio",title);
+				audio_player_view_.queueAudio(title_filename,id,title,visited_previously);
+				audio_player_view_.queueAudio(text_filename,id,text);
+				audio_player_view_.queueAudio(ding_filename,id,"[Audio Bing]");
+				
+				audio_player_view_.updateTextViewIfHeadIsNewLocation();
+			}
+		}
+    	
+    	return true;
+	}
+	
+	
+	public static void stopOverlayThreads()
+	{
+	   	 if (circlesOverlay!=null) { 
+	        	circlesOverlay.stop();
+	        	circlesOverlay = null;
+	        }
+	       
+	        if (itemizedMarkerOverlay!=null) {
+	       	 itemizedMarkerOverlay.stop();
+	        	itemizedMarkerOverlay = null;
+	        }
+
+	        if (routeOverlay!=null) {
+	       	 routeOverlay.stop();
+	        	routeOverlay = null;
+	        }
+
+	        if (routeMarkerOverlay!=null) {
+	       	 routeMarkerOverlay.stop();
+	        	routeMarkerOverlay = null;
+	        }
+	}
+    private static class UtteranceWaitListener implements OnUtteranceCompletedListener {
+        private boolean mIsComplete = false;
+        private final String mExpectedUtterance;
+
+        public UtteranceWaitListener(String expectedUtteranceId) {
+            mExpectedUtterance = expectedUtteranceId;
+        }
+
+        public void onUtteranceCompleted(String utteranceId) {
+            if (mExpectedUtterance.equals(utteranceId)) {
+                synchronized(this) {
+                    mIsComplete = true;	
+                    notify();
+                }
+            }
+        }
+        
+        public boolean waitForComplete() throws InterruptedException {
+            if (mIsComplete) {	
+                return true;
+            }
+
+            synchronized (this) {
+                int TTS_SPEECH_MAX_WAIT_TIME = 5* 1000;
+            	wait(TTS_SPEECH_MAX_WAIT_TIME);
+                return mIsComplete;
+            }
+        }
+        
+    }    
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleActivity.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleActivity.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleActivity.java	(revision 24268)
@@ -0,0 +1,557 @@
+package org.greenstone.android.tipple;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Locale;
+
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.AssetManager;
+import android.location.LocationListener;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+import org.mapsforge.android.maps.MapActivity;
+import org.mapsforge.android.maps.MapView;
+
+
+public class TippleActivity extends MapActivity implements OnInitListener
+{
+	enum TextAudioModeEnum { TEXT_ONLY, TEXT_PLUS_AUDIO, AUDIO_ONLY, AUDIO_PLUS_TEXT };
+	
+	// File storage
+	public static String geodataDirectory;
+	public static String logDirectory;
+	public static String audioDirectory;
+	
+	// Preferences
+	public static SharedPreferences sharedPreferences;
+	public static boolean centreOnGPSLocation;
+	public static boolean showScaleBar; 
+	public static boolean showSights;
+	public static boolean showUserTrail;
+	public static TextAudioModeEnum textAudioMode;
+	
+	// Logging
+	public static TippleLog log;
+	
+	// Views
+	protected TextView 		  longlat_view_;
+	protected MapView  		  map_view_;
+	protected TextView        text_view_;
+	protected FrameLayout     text_map_composite_;
+	
+	protected AudioPlayerView audio_player_view_;
+	
+	protected TipLocationManager tip_location_manager_;
+	
+	protected ArrayList<TipLocation> locations_ = null;
+	protected ArrayList<TipLocation> user_trail_ = null;
+	protected String user_trail_filename_ = null;
+	
+	// Text to Speech
+	protected TextToSpeech tts_ = null;
+	
+	protected LocationManager location_manager_ = null;
+	protected LocationListener location_listener_ = null; 
+	
+	// Request codes
+	protected final int TTS_DATA_CHECK = 0;
+	private static final int SELECT_LOG_FILE = 1;
+	
+	
+	protected boolean assetToExternalStorage(String asset_filename, String external_storage_filename)
+	{
+		boolean status = true;
+		
+		try {
+			
+			File external_storage_file = new File(external_storage_filename);
+			if (!external_storage_file.exists()) {
+				// Need to create the /sdcard version
+				
+				AssetManager assetManager = getAssets();
+				
+				InputStream ais = assetManager.open(asset_filename); // ais = asset input stream
+				FileOutputStream fos = new FileOutputStream(external_storage_file); // fos = file output stream
+			
+				byte [] buffer = new byte[1024];
+				
+				int buflen;
+				while ((buflen = ais.read(buffer))>0) {
+			      fos.write(buffer,0,buflen);
+				}
+			    
+				ais.close();
+				fos.close();
+			}
+			
+	    } 
+		catch (Exception e) {
+			e.printStackTrace();
+			status = false;
+		}			
+	    
+		return status;
+	}
+	
+	protected boolean initTippleStore(String location)
+	{
+		
+    	// Locate map data directory
+        File external_root_file = Environment.getExternalStorageDirectory();
+        String external_root = external_root_file.getAbsolutePath();
+        String tipple_store_dir = external_root + "/tipple-store";
+        
+        geodataDirectory = tipple_store_dir + "/geodata/";
+        logDirectory = tipple_store_dir + "/logs/";
+        audioDirectory = tipple_store_dir + "/audio/";
+		
+        File tipple_store_file = new File(tipple_store_dir);
+        if (!tipple_store_file.exists()) {
+        	if (!tipple_store_file.mkdir()) {
+        		System.err.println("Failed to create directory: " + tipple_store_dir);
+        		return false;
+        	}
+        }
+        
+        File geodataDirFile = new File(geodataDirectory);
+        if (!geodataDirFile.exists()) {
+        	if (!geodataDirFile.mkdir()) {
+        		System.err.println("Failed to create directory: " + geodataDirectory);
+        		return false;
+        	}
+        }
+        
+        File logDirFile = new File(logDirectory);
+        if (!logDirFile.exists()) {
+        	if (!logDirFile.mkdir()) {
+        		System.err.println("Failed to create directory: " + logDirectory);
+        		return false;
+        	}
+        }
+        
+        File audioDirFile = new File(audioDirectory);
+        if (!audioDirFile.exists()) {
+        	if (!audioDirFile.mkdir()) {
+        		System.err.println("Failed to create directory: " + audioDirectory);
+        		return false;
+        	}
+        }
+        
+        // dig out the audio files
+        String[] audio_filenames = { "BeepMorseSonar.wav","Ding.wav","DingExtended.wav" };
+        for (String audio_filename: audio_filenames) {
+        	String audio_asset_filename = "audio/" + audio_filename; 
+    		String audio_exstore_filename = audioDirectory + audio_filename;
+    		
+    		if (!assetToExternalStorage(audio_asset_filename,audio_exstore_filename)) {
+    			System.err.println("Failed to transfer asset " + audio_asset_filename + " to " + audio_exstore_filename);
+    			return false;
+    		}
+        }
+        
+        if ((location != null) && (!location.equals(""))) {
+        	// get out map and XML location file
+        
+        	String map_asset_filename = "geodata/" + location + ".map";
+    		String map_exstore_filename = geodataDirectory + location + ".map";
+    		
+    		if (!assetToExternalStorage(map_asset_filename,map_exstore_filename)) {
+    			System.err.println("Failed to transfer asset " + map_asset_filename + " to " + map_exstore_filename);
+    			return false;
+    		}
+    		
+    		String loc_asset_filename = "geodata/" + location + ".loc";
+    		String loc_exstore_filename = geodataDirectory + location + ".loc";
+    		
+    		if (!assetToExternalStorage(loc_asset_filename,loc_exstore_filename)) {
+    			System.err.println("Failed to transfer asset " + loc_asset_filename + " to " + loc_exstore_filename);
+    			return false;
+    		}	
+        }
+		
+        return true;
+	}
+	
+	protected boolean initTippleStore()
+	{
+		return initTippleStore(null);
+	}
+	protected TextView createLongLatView()
+	{
+		// Lat,Long line at top
+        TextView longlat_view = new TextView(this);
+        longlat_view.setText("(Long,Lat): ");
+        
+        LayoutParams longlat_view_layout 
+    	= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 35, 0);
+        longlat_view.setLayoutParams(longlat_view_layout);
+        
+        return longlat_view;
+	}
+	
+	
+	
+	
+	protected MapView createMapView(String map_filename)
+	{
+		// Map view in middle
+        MapView map_view = new MapView(this);
+        map_view.setClickable(true);
+        map_view.setBuiltInZoomControls(true);
+        String full_map_filename = geodataDirectory + map_filename;
+           
+        map_view.setMapFile(full_map_filename);
+       
+        LayoutParams map_view_layout 
+			= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 
+											ViewGroup.LayoutParams.WRAP_CONTENT, 1);
+        
+        map_view.setLayoutParams(map_view_layout);
+            
+     
+        return map_view;
+	}
+	
+	protected MapView createMapViewFromAsset(String map_filename)
+	{
+		// Transfer asset to file in dataDirectory if it does not already exist
+	
+		String asset_filename = "geodata" + File.separator + map_filename;
+		String external_storage_filename = geodataDirectory + map_filename;
+		
+		assetToExternalStorage(asset_filename,external_storage_filename);
+		
+		return createMapView(map_filename);
+	 
+	}
+	
+	protected TextView createTextView()
+	{
+		// Text view in middle (ultimately on top of map view)
+        TextView text_view = new TextView(this);
+        
+        LayoutParams text_view_layout 
+			= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
+											ViewGroup.LayoutParams.WRAP_CONTENT, 1);
+        text_view.setLayoutParams(text_view_layout);
+               
+        text_view.setGravity(Gravity.BOTTOM|Gravity.FILL_HORIZONTAL);
+        
+        text_view.setPadding(12, 12, 12, 12);
+        text_view.setTextColor(0xffffffff);
+        text_view.setBackgroundColor(0xCC000000);
+        text_view.setTextSize(20.0f);
+        
+        text_view.setVerticalScrollBarEnabled(true);
+        text_view.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
+        
+        text_view.setMovementMethod(new ScrollingMovementMethod());
+      
+        text_view.setVisibility(View.INVISIBLE);
+        
+        return text_view;
+	}
+	
+	protected FrameLayout compositTextViewOnMapView(MapView map_view, TextView text_view)
+	{
+		FrameLayout frame_layout = new FrameLayout(this);
+		
+		 LayoutParams view_layout 
+			= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 
+											ViewGroup.LayoutParams.WRAP_CONTENT, 1);
+
+		 frame_layout.setLayoutParams(view_layout);
+		 frame_layout.addView(map_view);
+		 frame_layout.addView(text_view);
+		 
+		return frame_layout;
+	}
+	
+	protected AudioPlayerView createAudioPlayerView()
+	{
+		  // Player at the bottom
+        AudioPlayerView audio_player_view = new AudioPlayerView(this, text_view_);
+  
+        LayoutParams audio_player_layout 
+			= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,100, 0);
+        audio_player_view.setLayoutParams(audio_player_layout);
+	
+        return audio_player_view;
+	}
+	
+	
+
+	protected void refreshPreferences() 
+	{
+		// Restore preferences
+		sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+		
+        //shared_preferences_ = getPreferences(MODE_PRIVATE);
+        showScaleBar = sharedPreferences.getBoolean("showScaleBar", false);
+        centreOnGPSLocation = sharedPreferences.getBoolean("centreOnGPSLoc", false);
+        
+        showSights = sharedPreferences.getBoolean("showSights", true);
+        showUserTrail = sharedPreferences.getBoolean("showUserTrail", false);
+        
+        String textAudioModeStr = sharedPreferences.getString("textAudioMode", "AUDIO_PLUS_TEXT");
+		textAudioMode = Enum.valueOf(TextAudioModeEnum.class, textAudioModeStr);
+	}
+	
+	
+    @Override
+    protected void onCreate(Bundle savedInstanceState) 
+    {
+    	System.err.println("*** TippleActivity::onCreate()");
+        super.onCreate(savedInstanceState);
+    
+        if (!initTippleStore("hamilton")) {
+        	System.err.println("Failed to initialize TippleStore on sdcard.  Quiting");
+        	onDestroy();
+        }
+        
+        // Set up logging
+        log = new TippleLog(this,logDirectory);
+        log.optStartLog();
+       
+       // "refresh" preferences here so views can be configured correctly
+       // and ensure showUserTrail is reset to off (as not log file has been selected yet)	
+       refreshPreferences(); 
+       SharedPreferences.Editor editor = sharedPreferences.edit();
+       editor.putBoolean("showUserTrail",false);
+       
+        // Set up Text to Speech
+    	Intent checkIntent = new Intent();
+    	checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
+    	startActivityForResult(checkIntent, TTS_DATA_CHECK);
+    	   	
+    	//startActivityForResult(new Intent(this, LogFileBrowser.class), SELECT_LOG_FILE);
+    	 
+    	
+    	// Screen dived into:
+    	// Top:    GPS location 
+    	// Middle: Map view
+    	// Bottom: Media player
+    
+		LinearLayout linearLayout = new LinearLayout(this);
+		linearLayout.setOrientation(LinearLayout.VERTICAL);
+		
+		longlat_view_ = createLongLatView();
+        //map_view_ = createMapView("new-zealand.map");
+        map_view_ = createMapViewFromAsset("hamilton.map");
+        
+        text_view_ = createTextView();
+        text_map_composite_ = compositTextViewOnMapView(map_view_,text_view_);
+        
+        audio_player_view_ = createAudioPlayerView();
+        
+		linearLayout.addView(longlat_view_);
+        linearLayout.addView(text_map_composite_);
+        linearLayout.addView(audio_player_view_);
+        
+        setContentView(linearLayout); 
+    }
+    
+    
+    @Override
+	protected void onResume() 
+    {
+    	System.err.println("*** TippleActivity::onResume()");
+    	
+		super.onResume();
+		
+		refreshPreferences();
+		audio_player_view_.refreshView();
+		
+        map_view_.setScaleBar(showScaleBar);
+        
+        if (tip_location_manager_ != null) {
+        	 if (showSights) {
+        		//tip_location_manager_.createTourLocationsOverlay(map_view_,locations_);
+             	tip_location_manager_.addTourLocations(map_view_);
+             }
+             else {
+             	tip_location_manager_.removeTourLocations(map_view_);
+             }
+             
+             if (user_trail_filename_ != null) {
+             	
+             	if (showUserTrail) {
+             		//tip_location_manager_.createLogRouteLocationsOverlay(map_view_,user_trail_);
+             		tip_location_manager_.addLogRouteLocations(map_view_);
+             	}
+             	else {
+             		tip_location_manager_.removeLogRouteLocations(map_view_);
+             	}
+             }
+        }
+        
+    } 
+		
+    
+		
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
+    {
+    	System.err.println("*** TippleActivity::onActivityResult(), requestCode: " + requestCode);
+    	
+	    if (requestCode == TTS_DATA_CHECK) {
+	        if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
+	            // success, create the TTS instance
+	            tts_ = new TextToSpeech(this, (TextToSpeech.OnInitListener) this);
+	        	tts_.setLanguage(Locale.UK);
+	        	
+	            // now TTS initialized, init TTS aware place-locations
+	            String loc_filename = geodataDirectory + "hamilton.loc";
+	            tip_location_manager_ = new TipLocationManager(this,audio_player_view_,tts_);
+	            locations_ = tip_location_manager_.loadTour(loc_filename);
+	            tip_location_manager_.createTourLocationsOverlay(map_view_,locations_);
+	            
+	            // Set up a callback method for handling GPS location updates connected with our place-locations   
+	    		location_manager_ = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
+	    		location_listener_ 
+	    			= new TippleLocationListener(this,map_view_,longlat_view_,tip_location_manager_,
+	    										 locations_);
+	    		
+	    		location_manager_.requestLocationUpdates(LocationManager.GPS_PROVIDER,0, 0, location_listener_);
+	            
+	        } else {
+	            // missing data, install it
+	            Intent installIntent = new Intent();
+	            installIntent.setAction(
+	                TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
+	            startActivity(installIntent);
+	        }
+	    }
+	   
+	    else if (requestCode == SELECT_LOG_FILE) {
+	    	
+			if (resultCode == RESULT_OK) {
+				if (data != null && data.getStringExtra("logFile") != null) {
+					if (user_trail_filename_ != null) {
+						// have a previously loaded route => scrub it
+						tip_location_manager_.removeLogRouteLocations(map_view_);
+						user_trail_filename_ = null;
+					}
+					// set up route overlay
+					user_trail_filename_ = data.getStringExtra("logFile");
+					user_trail_ = tip_location_manager_.loadUserTrail(user_trail_filename_);
+		        	tip_location_manager_.createLogRouteLocationsOverlay(map_view_,user_trail_);
+				}
+			} else if (resultCode == RESULT_CANCELED) {
+				// nothing to do
+			}
+			
+		}
+	    
+	}
+    
+    
+    @Override
+	public void onInit(int status) 
+    {
+    	System.err.println("*** TippleActivity::onInit(), status (zero == good): " + status);
+    	
+		if (status == TextToSpeech.SUCCESS) {
+			Log.d("TippleActivity::onInit()","Text-to-speech engine initialized");
+		}
+		else if (status == TextToSpeech.ERROR){
+			Log.e("TippleActivity::onInit()","Text-to-speech engine failed initialized");
+			System.err.println("Error occured while initializing text-to-speech engine");
+		}
+
+	}
+    
+    @Override
+	public boolean onCreateOptionsMenu(Menu menu) 
+    {
+		getMenuInflater().inflate(R.menu.options_menu, menu);
+		return true;
+	}
+    
+    @Override
+	public boolean onOptionsItemSelected(MenuItem item) 
+    {
+		switch (item.getItemId()) {
+			case R.id.menu_info:
+				startActivity(new Intent(this, InfoView.class));
+				return true;
+
+			case R.id.menu_preferences:
+				startActivity(new Intent(this, EditPreferences.class));
+				return true;
+				
+			case R.id.menu_logfile:
+				startActivityForResult(new Intent(this, TippleLogFileBrowser.class), SELECT_LOG_FILE);
+				return true;
+				
+			default:
+				return false;
+		}
+	}
+
+    
+    
+    @Override
+    protected void onStop()
+    {
+     	System.err.println("*** TippleActivity::onStop()");
+        super.onStop();
+
+        // Is this needed?
+        // Go through an Editor object to make preference changes stick
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.commit();
+        
+    }
+    
+    @Override
+    protected void onDestroy()
+    {
+     	System.err.println("*** TippleActivity::onDestroy()");  
+        super.onDestroy();
+        
+        tts_.shutdown();
+        tts_ = null;
+        
+    	// Switch off location updates
+        if (location_manager_ != null) {
+        	
+        	if (location_listener_ != null) {
+			
+				location_manager_.removeUpdates(location_listener_);
+				location_listener_ = null;
+			}
+			location_manager_ = null;
+			
+			user_trail_filename_ = null;
+		}
+	
+     
+       TipLocationManager.stopOverlayThreads();
+ 
+  
+       log.optStopLog();     
+    }
+
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLocationListener.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLocationListener.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLocationListener.java	(revision 24268)
@@ -0,0 +1,296 @@
+package org.greenstone.android.tipple;
+
+import java.io.File;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.greenstone.android.tipple.TipLocation;
+
+import org.mapsforge.android.maps.ArrayItemizedMarkerOverlay;
+import org.mapsforge.android.maps.CircleOverlay;
+import org.mapsforge.android.maps.GeoPoint;
+import org.mapsforge.android.maps.MapController;
+import org.mapsforge.android.maps.MapView;
+import org.mapsforge.android.maps.OverlayMarkerItem;
+import org.mapsforge.android.maps.OverlayTextAudioItem;
+import org.mapsforge.android.maps.Projection;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.location.Location;
+import android.location.LocationListener;
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.os.Vibrator;
+
+import android.widget.TextView;
+
+public class TippleLocationListener implements LocationListener 
+{
+	public static Location lastKnownLocation = null;
+	
+	protected ArrayList<TipLocation> locations_;
+	
+	protected Activity activity_;
+	protected MapView map_view_;
+	protected TextView text_view_;
+	protected TipLocationManager tip_location_manager_;
+	
+	protected MapController map_controller_;
+	protected CircleOverlay here_overlay_;
+	protected ArrayItemizedMarkerOverlay highlight_marker_overlay_;
+	
+	protected Vibrator vibrator_;
+
+	protected Set<TipLocation> curr_locations_;
+	protected Set<TipLocation> prev_locations_;
+	HashMap<Integer,OverlayMarkerItem> highlight_marker_hash_;
+	
+
+	public TippleLocationListener(Activity activity, MapView map_view,TextView text_view,
+			TipLocationManager place_location_manager,
+			ArrayList<TipLocation> locations) 
+	{
+		activity_ = activity;
+		map_view_ = map_view;
+		text_view_ = text_view;
+		
+		tip_location_manager_ = place_location_manager;
+		locations_ = locations;
+		
+		// location aware
+		Paint circleFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+		circleFillPaint.setStyle(Paint.Style.FILL);
+		circleFillPaint.setColor(Color.GREEN);
+		circleFillPaint.setAlpha(64);
+
+		Paint circleOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+		circleOutlinePaint.setStyle(Paint.Style.STROKE);
+		circleOutlinePaint.setColor(Color.GREEN);
+		circleOutlinePaint.setAlpha(128);
+		circleOutlinePaint.setStrokeWidth(3);
+		here_overlay_ = new CircleOverlay(circleFillPaint, circleOutlinePaint);
+		map_view_.getOverlays().add(here_overlay_);
+
+		Drawable highlightMarker = activity_.getResources().getDrawable(R.drawable.markerhighlight);
+		highlight_marker_overlay_ = new ArrayItemizedMarkerOverlay(highlightMarker,activity);
+		map_view_.getOverlays().add(highlight_marker_overlay_);
+		
+		vibrator_ = (Vibrator) activity
+				.getSystemService(Context.VIBRATOR_SERVICE);
+		
+		curr_locations_ = new HashSet<TipLocation>();
+		prev_locations_ = new HashSet<TipLocation>();
+		highlight_marker_hash_ = new HashMap<Integer,OverlayMarkerItem>();
+		
+	}
+
+	boolean checking_location_ = false;
+	
+	synchronized protected boolean getLock()
+	{
+		if (!checking_location_) {
+			checking_location_ = true;
+		}
+		
+		return checking_location_;
+	}
+	
+	synchronized protected void freeLock()
+	{
+		checking_location_ = false;
+	}
+	
+	protected double pointDistance(Point place_point, Point here_point)
+	{
+		double delta_x = place_point.x - here_point.x;
+		double delta_y = place_point.y - here_point.y;
+		double pixel_distance = Math.sqrt((delta_x * delta_x) + (delta_y * delta_y));
+
+		return pixel_distance;
+	}
+	
+	Point lastLoggedPoint_ = null;
+	
+	protected void updateActiveLocations(Location loc)
+	{
+		// Find out if the current GPS location lies within the boundaries
+		// of any of the tour locations
+		Projection projection = map_view_.getProjection();
+		byte zoom_level = map_view_.getZoomLevel();
+		// byte zoom_level = 10; // work at a reasonable level of scale
+
+		float in_out_gap_meters = 3.0f;
+		float in_out_gap_pixels 
+			= projection.metersToPixels(in_out_gap_meters, zoom_level);
+
+		double here_latitude = loc.getLatitude();
+		double here_longitude = loc.getLongitude();
+		GeoPoint here_geopoint = new GeoPoint(here_latitude, here_longitude);
+		Point here_point = projection.toPoint(here_geopoint, null,zoom_level);
+		
+		if (lastLoggedPoint_==null) {
+			lastLoggedPoint_ = here_point;
+			TippleActivity.log.optMessage("GPS Location Reading");
+		}
+		else {
+			float far_enough_meters = 3.0f; // 3 meters
+			float far_enough_pixels = projection.metersToPixels(far_enough_meters, zoom_level);
+			if (pointDistance(here_point,lastLoggedPoint_)>far_enough_pixels) {
+				TippleActivity.log.optMessage("GPS Location Reading");
+			}
+		}
+		
+		for (int i=0; i<locations_.size(); i++) {
+
+			TipLocation place = locations_.get(i);
+			
+			double place_latitude = place.getLatitude();
+			double place_longitude = place.getLongitude();
+			double radius_meters = place.getRadiusKM() * 1000.0;
+
+			GeoPoint place_geopoint = new GeoPoint(place_latitude,place_longitude);
+			Point place_point = projection.toPoint(place_geopoint, null,zoom_level);
+
+			float radius_pixel_distance = projection.metersToPixels((float) radius_meters, zoom_level);
+
+			double delta_x = place_point.x - here_point.x;
+			double delta_y = place_point.y - here_point.y;
+			double pixel_distance = Math.sqrt((delta_x * delta_x) + (delta_y * delta_y));
+
+			double outside_pixel_distance = radius_pixel_distance + in_out_gap_pixels;
+			double inside_pixel_distance  = radius_pixel_distance;
+
+			if (pixel_distance <= inside_pixel_distance) {
+
+				if (!curr_locations_.contains(place)) {
+					// User has entered new location on tour
+					
+					curr_locations_.add(place);
+					vibrator_.vibrate(1000);
+					Bundle bundle = new Bundle();
+					bundle.putString("placeName", place.getTitle());
+
+					boolean visited_previously = prev_locations_.contains(place);
+					tip_location_manager_.textToSpeechToLocation(place,visited_previously);
+					
+					OverlayTextAudioItem here_marker_item 
+						= (OverlayTextAudioItem) TipLocationManager.itemizedMarkerOverlayArrayList.get(i);
+					
+					GeoPoint dm_geopoint = here_marker_item.getPoint();
+					String title = here_marker_item.getTitle();
+					
+					//itemized_overlay.onTap(i);
+					
+					OverlayMarkerItem highlight_marker_item = new OverlayMarkerItem(dm_geopoint,title);
+					highlight_marker_hash_.put(i,highlight_marker_item);
+					highlight_marker_overlay_.addOverlay(highlight_marker_item);
+					
+					
+					try {
+						// Cue up audio alert
+						String audio_dir = TippleActivity.audioDirectory;
+						File alert_file = new File(audio_dir, "BeepMorseSonar.wav");
+						String alert_filename = alert_file.getAbsolutePath();
+					
+						MediaPlayer mp = new MediaPlayer();
+						mp.setDataSource(alert_filename);
+						mp.prepare();
+						mp.start();
+					} 
+					catch (Exception e) {
+						System.err.println("Failed to play audio alert for active GPS location");
+						e.printStackTrace();
+					}
+					
+					// Now record the fact we've been here
+					prev_locations_.add(place);
+						
+				}
+
+			}
+
+			else if (pixel_distance >= outside_pixel_distance) {
+				
+				if (curr_locations_.contains(place)) {
+					curr_locations_.remove(place);
+					
+					// TipLocationManager.itemizedMarkerOverlay.unTap();
+					
+					OverlayMarkerItem highlight_marker_item = highlight_marker_hash_.remove(i);
+					highlight_marker_overlay_.removeOverlay(highlight_marker_item);
+				}
+			}
+		}
+	}
+	@Override
+	public void onLocationChanged(Location loc) 
+	{
+		if (loc != null) {
+
+			//System.err.println("TipLocationListener::onLocationChaged -- start");
+			lastKnownLocation = loc;
+			
+			if (getLock()) {
+				updateActiveLocations(loc);
+				freeLock();
+			}
+
+			DecimalFormat truncPlaces = new DecimalFormat("0.0000000000");
+			double longitude = loc.getLongitude();
+			double latitude = loc.getLatitude();
+			
+			String trunc_long = truncPlaces.format(longitude);
+			String trunc_lat  = truncPlaces.format(latitude);
+			
+			String text_mess = "(Long,Lat): (" +  trunc_long + "," + trunc_lat + ")";
+			text_view_.setText(text_mess);
+
+			// location aware
+			GeoPoint point = new GeoPoint(latitude, longitude);
+			here_overlay_.setCircleData(point, loc.getAccuracy());
+			
+			// Use the following line if you want to map to auto center on the GPS location
+			 boolean centre_on_gps_loc = TippleActivity.sharedPreferences.getBoolean("centreOnGPSLoc", false);
+			 if (centre_on_gps_loc) {
+				 MapController map_controller = map_view_.getController();
+				 if (map_controller != null) {
+					 	map_controller.setCenter(point);
+				 }
+			 }
+			 
+			//System.err.println("TipLocationListener::onLocationChaged -- finished");
+		}
+	}
+
+	@Override
+	public void onProviderDisabled(String provider) 
+	{
+		// TODO Auto-generated method stub
+		System.err.println("TipLocationListener::onProviderDisabled()");
+	}
+
+	@Override
+	public void onProviderEnabled(String provider)
+	{
+		// TODO Auto-generated method stub
+		System.err.println("TipLocationListener::onProviderEnabled()");
+
+	}
+
+	@Override
+	public void onStatusChanged(String provider, int status, Bundle extras) 
+	{
+		// TODO Auto-generated method stub
+		System.err.println("TipLocationListener::onStatusChanged()");
+
+	}
+
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLog.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLog.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLog.java	(revision 24268)
@@ -0,0 +1,189 @@
+package org.greenstone.android.tipple;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.text.DecimalFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.location.Location;
+
+public class TippleLog 
+{
+	protected Activity activity_;
+	protected String log_directory_;
+	
+	public PrintStream TIPLOG = null;
+	protected boolean logging_enabled_ = false;
+	protected File    logging_file_;
+	protected String  logging_filename_final_;
+
+	
+	public TippleLog(Activity activity, String logDirectory) 
+	{
+		activity_ = activity;
+		log_directory_ = logDirectory;
+		
+		//boolean logfile_ok = initLogging();
+	    //alertLogging(logfile_ok);
+	}
+	
+	protected void optStartLog()
+	{
+	    if (TIPLOG!=null) {
+	    	   TIPLOG.println("<Log>");
+	    }
+	}
+	
+	protected void optStopLog()
+	{
+	    if (TIPLOG != null) {
+	    	   TIPLOG.println("</Log>");
+	    	   
+	    	   TIPLOG.close();
+	    	   
+	    	   
+	    	   File logging_file_final = new File(logging_filename_final_);
+	    	   logging_file_.renameTo(logging_file_final);
+	    	   
+	    	  
+	    }
+	}
+	
+	public void optMessage(String action, String message_line)
+	{
+		if (logging_enabled_) {
+			
+			TIPLOG.println("  <LogEntry>");
+			
+			final long timestamp = System.currentTimeMillis();
+			final Calendar cal = Calendar.getInstance();
+			cal.setTimeInMillis(timestamp);
+			Date date = cal.getTime();
+			int hour = date.getHours();
+			int minute = date.getMinutes();
+			int seconds = date.getSeconds();
+			
+			TIPLOG.println("    <Timestamp>"+timestamp+"</Timestamp>");
+			TIPLOG.println("    <Time>" + hour + ":" + minute + ":" + seconds + "</Time>");
+			
+			Location loc = TippleLocationListener.lastKnownLocation;
+			if (loc!=null) {
+				double longitude = loc.getLongitude();
+				double latitude  = loc.getLatitude();
+				double accuracy  = loc.getAccuracy();
+				TIPLOG.println("    <Longitude>"+longitude+"</Longitude>");
+				TIPLOG.println("    <Latitude>"+latitude+"</Latitude>");
+				TIPLOG.println("    <Accuracy>"+accuracy+"</Accuracy>");
+			}
+			TIPLOG.println("    <Action>"+action+"</Action>");
+			if (message_line != "") {
+				TIPLOG.println("    <Message>"+message_line+"</Message>");
+			}
+			TIPLOG.println("  </LogEntry>");
+		}
+	}
+	
+	public void optMessage(String action)
+	{
+		optMessage(action,"");
+	}
+	
+
+	 DialogInterface.OnClickListener contEnableCallback = new DialogInterface.OnClickListener() {
+    	public void onClick(DialogInterface dialog, int which) {
+    		logging_enabled_ = true;
+    		dialog.cancel();
+    		//dialog.notifyAll();
+    	}
+    };
+    
+    DialogInterface.OnClickListener contDisableCallback = new DialogInterface.OnClickListener() {
+     	public void onClick(DialogInterface dialog, int which) {
+     		
+     		// Delete logging file, then allow application to proceed 
+     		logging_file_.delete();
+     		logging_file_ = null;
+     		TIPLOG = null;
+     		
+     		logging_enabled_ = false;
+     		
+     		//SharedPreferences.Editor editor = activity_.sharedPreferences.edit();
+     		//editor.putBoolean("showUserTrail", false);
+     		//editor.commit();
+     		
+     		dialog.cancel();
+     	}
+     };
+     
+    DialogInterface.OnClickListener stopCallback = new DialogInterface.OnClickListener() {
+		public void onClick(DialogInterface dialog, int which) {
+			
+			// Delete logging file, then finish application
+			
+     		logging_file_.delete();
+     		logging_file_ = null;
+     		TIPLOG = null;
+     		
+			logging_enabled_ = false;
+			
+			activity_.finish();
+		}
+	};
+	
+	protected boolean initLogging()
+	{
+		 boolean logfile_ok = false;
+        
+        try {
+        	
+        	for (int i=1; i<1000; i++) {
+        		
+        		DecimalFormat zeroFill = new DecimalFormat("000");
+        		String formatted_i = zeroFill.format(i);
+        		
+        		File potential_log_file = new File(log_directory_,"UserInteract-" + formatted_i + ".log");
+        		
+        		if (potential_log_file.exists()) { continue; }
+        		
+        		logging_file_ = new File(log_directory_,"UserInteract-" + formatted_i);
+        		logging_filename_final_ = logging_file_.getAbsolutePath() + ".log";
+        		
+        		TIPLOG = new PrintStream(logging_file_);
+        		if (TIPLOG != null) {
+        			logfile_ok = true;
+        			break;
+        		}
+        	}
+        }
+        catch (Exception e) {
+       	 logging_file_ = null;
+        	 e.printStackTrace();      
+        }
+        
+        return logfile_ok;
+	}
+	
+	protected void alertLogging(boolean logfile_ok)
+	{   		
+	    	AlertDialog.Builder confirm_log_alert = new AlertDialog.Builder(activity_);
+	    	
+	    	if (logfile_ok) {
+	    		confirm_log_alert.setTitle("Logging Activity");
+		        confirm_log_alert.setMessage("Tipple can log your user interaction.  Do you want this activated?");
+		        confirm_log_alert.setPositiveButton(R.string.logging_ok_enable,contEnableCallback);
+		        confirm_log_alert.setNegativeButton(R.string.logging_ok_disable,contDisableCallback);	
+	    	}
+	    	else {
+	    		confirm_log_alert.setTitle("Error");
+		        confirm_log_alert.setMessage("Failed to initialize user activity log file");
+		        confirm_log_alert.setPositiveButton(R.string.logging_error_continue,contDisableCallback);
+		        confirm_log_alert.setNegativeButton(R.string.logging_error_stop,stopCallback);		
+	    	}
+	    	
+	    	confirm_log_alert.show();
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLogFileBrowser.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLogFileBrowser.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLogFileBrowser.java	(revision 24268)
@@ -0,0 +1,207 @@
+/*
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Based on FileBrowser.java from mapsforge.org
+ * 
+ */
+
+package org.greenstone.android.tipple;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.GridView;
+
+/**
+ * Simple file browser activity to select a log file from the file system.
+ */
+public class TippleLogFileBrowser extends Activity implements AdapterView.OnItemClickListener 
+{
+	private static final String DEFAULT_DIRECTORY = "/";
+	private static final int DIALOG_LOG_FILE_INVALID = 0;
+	private static final int DIALOG_LOG_FILE_SELECT = 1;
+	private static final String PREFERENCES_FILE = "FileBrowser";
+	private File currentDirectory;
+	private TippleLogFileBrowserIconAdapter fileBrowserIconAdapter;
+	private Comparator<File> fileComparator;
+	private FileFilter fileFilter;
+	private File[] files;
+	private File[] filesWithParentFolder;
+	private GridView gridView;
+	private File selectedFile;
+
+	@Override
+	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long id) {
+		this.selectedFile = this.files[(int) id];
+		if (this.selectedFile.isDirectory()) {
+			this.currentDirectory = this.selectedFile;
+			browseToCurrentDirectory();
+		} else if (this.selectedFile.getAbsolutePath().endsWith(".log")) {
+			String log_filename = selectedFile.getAbsolutePath();
+			Intent ok_intent = new Intent();
+			ok_intent.putExtra("logFile", log_filename);
+					
+			setResult(RESULT_OK, ok_intent);
+			finish();
+		} else {
+			showDialog(DIALOG_LOG_FILE_INVALID);
+		}
+	}
+
+	@Override
+	public boolean onKeyDown(int keyCode, KeyEvent event) {
+		if (keyCode == KeyEvent.KEYCODE_BACK) {
+			// quit the application
+			setResult(RESULT_CANCELED);
+			finish();
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Browse to the current directory.
+	 */
+	private void browseToCurrentDirectory() {
+		setTitle(this.currentDirectory.getAbsolutePath());
+
+		// read and filter files and subfolders in the current folder
+		this.files = this.currentDirectory.listFiles(this.fileFilter);
+
+		// order all files by type and alphabetically by name
+		Arrays.sort(this.files, this.fileComparator);
+
+		// if a parent directory exists, add it at the first position
+		if (this.currentDirectory.getParentFile() != null) {
+			this.filesWithParentFolder = new File[this.files.length + 1];
+			this.filesWithParentFolder[0] = this.currentDirectory.getParentFile();
+			System.arraycopy(this.files, 0, this.filesWithParentFolder, 1, this.files.length);
+			this.files = this.filesWithParentFolder;
+			this.fileBrowserIconAdapter.updateFiles(this.files, true);
+		} else {
+			this.fileBrowserIconAdapter.updateFiles(this.files, false);
+		}
+		this.fileBrowserIconAdapter.notifyDataSetChanged();
+	}
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.filebrowser);
+		this.fileBrowserIconAdapter = new TippleLogFileBrowserIconAdapter(this);
+		this.gridView = (GridView) findViewById(R.id.fileBrowserView);
+		this.gridView.setOnItemClickListener(this);
+		this.gridView.setAdapter(this.fileBrowserIconAdapter);
+
+		this.fileFilter = new FileFilter() {
+			@Override
+			public boolean accept(File pathname) {
+				if (pathname.isDirectory()) {
+					// accept all readable folders
+					return pathname.canRead();
+				}
+				// accept all readable files with the correct extension
+				return pathname.canRead() && pathname.getName().endsWith(".log");
+			}
+		};
+
+		this.fileComparator = new Comparator<File>() {
+			@Override
+			public int compare(File o1, File o2) {
+				if (o1.isDirectory() && !o2.isDirectory()) {
+					return -1;
+				} else if (!o1.isDirectory() && o2.isDirectory()) {
+					return 1;
+				} else {
+					return o1.getName().compareToIgnoreCase(o2.getName());
+				}
+			}
+		};
+
+		/*
+		if (savedInstanceState == null) {
+			// first start of this instance
+			showDialog(DIALOG_LOG_FILE_SELECT);
+		}
+		 
+		 */
+	}
+
+	@Override
+	protected Dialog onCreateDialog(int id) {
+		AlertDialog.Builder builder = new AlertDialog.Builder(this);
+		switch (id) {
+			case DIALOG_LOG_FILE_INVALID:
+				builder.setMessage(getString(R.string.log_file_invalid)).setTitle(
+						getString(R.string.error)).setPositiveButton(getString(R.string.ok),
+						null);
+				return builder.create();
+			case DIALOG_LOG_FILE_SELECT:
+				builder.setMessage(getString(R.string.log_file_select)).setPositiveButton(
+						getString(R.string.ok), null);
+				return builder.create();
+			default:
+				return null;
+		}
+	}
+
+	@Override
+	protected void onPause() {
+		super.onPause();
+		// save the current directory
+		Editor editor = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE).edit();
+		editor.clear();
+		if (this.currentDirectory != null) {
+			editor.putString("currentDirectory", this.currentDirectory.getAbsolutePath());
+		}
+		editor.commit();
+	}
+
+	@Override
+	protected void onResume() {
+		super.onResume();
+		// check if the full screen mode should be activated
+		if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fullscreen", false)) {
+			getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+			getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+		} else {
+			getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+			getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
+		}
+
+		// restore the current directory
+		SharedPreferences preferences = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE);
+		this.currentDirectory = new File(preferences.getString("currentDirectory",
+				DEFAULT_DIRECTORY));
+		if (!this.currentDirectory.exists() || !this.currentDirectory.canRead()) {
+			this.currentDirectory = new File(DEFAULT_DIRECTORY);
+		}
+		browseToCurrentDirectory();
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLogFileBrowserIconAdapter.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLogFileBrowserIconAdapter.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/TippleLogFileBrowserIconAdapter.java	(revision 24268)
@@ -0,0 +1,113 @@
+/*
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * 
+ * Based on FileBrowserIconAdapter.java from mapsforge.org
+ * 
+ */
+package org.greenstone.android.tipple;
+
+import java.io.File;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+/**
+ * An adapter for the FileBrowser GridView.
+ */
+class TippleLogFileBrowserIconAdapter extends BaseAdapter 
+{
+	private Context context;
+	private File currentFile;
+	private File[] files;
+	private boolean hasParentFolder;
+	private TextView textView;
+
+	/**
+	 * Create a new FileBrowserIconAdapter with the given context.
+	 * 
+	 * @param context
+	 *            the context of this adapter, through which new Views can be created.
+	 */
+	TippleLogFileBrowserIconAdapter(Context context) {
+		this.context = context;
+	}
+
+	@Override
+	public int getCount() {
+		if (this.files == null) {
+			return 0;
+		}
+		return this.files.length;
+	}
+
+	@Override
+	public Object getItem(int index) {
+		return this.files[index];
+	}
+
+	@Override
+	public long getItemId(int index) {
+		return index;
+	}
+
+	@Override
+	public View getView(int index, View convertView, ViewGroup parent) {
+		if (convertView instanceof TextView) {
+			// recycle the old view
+			this.textView = (TextView) convertView;
+		} else {
+			// create a new view object
+			this.textView = new TextView(this.context);
+			this.textView.setLines(2);
+			this.textView.setGravity(Gravity.CENTER_HORIZONTAL);
+			this.textView.setPadding(5, 10, 5, 10);
+		}
+
+		if (index == 0 && this.hasParentFolder) {
+			// the parent directory of the current folder
+			this.textView.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_menu_back,
+					0, 0);
+			this.textView.setText("..");
+		} else {
+			this.currentFile = this.files[index];
+			if (this.currentFile.isDirectory()) {
+				this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
+						R.drawable.ic_menu_archive, 0, 0);
+			} else {
+				this.textView.setCompoundDrawablesWithIntrinsicBounds(0,
+						R.drawable.ic_menu_mapmode, 0, 0);
+			}
+			this.textView.setText(this.currentFile.getName());
+		}
+		return this.textView;
+	}
+
+	/**
+	 * Call this method to change the data of the adapter.
+	 * 
+	 * @param newFiles
+	 *            the new files for this adapter.
+	 * @param newHasParentFolder
+	 *            true if the files array has a parent folder at index 0, false otherwise.
+	 */
+	void updateFiles(File[] newFiles, boolean newHasParentFolder) {
+		this.files = newFiles;
+		this.hasParentFolder = newHasParentFolder;
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/XMLToHashmapHandler.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/XMLToHashmapHandler.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/greenstone/android/tipple/XMLToHashmapHandler.java	(revision 24268)
@@ -0,0 +1,79 @@
+package org.greenstone.android.tipple;
+
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class XMLToHashmapHandler extends DefaultHandler 
+{
+	String lockon_element_;
+	Boolean locked_on_;
+	ArrayList<HashMap<String,String>> lockon_list_; 
+	
+	HashMap<String,String> hashmap_;
+	String active_element_;
+	StringBuffer inner_text_;
+	
+	public XMLToHashmapHandler(String lockon_element)
+	{
+		// initial settings to parse a file	
+		lockon_element_ = lockon_element;
+		lockon_list_ = new ArrayList<HashMap<String,String>>();
+		locked_on_ = false;
+	}
+	
+	public ArrayList<HashMap<String,String>> getHashmapList() 
+	{
+		return lockon_list_;
+	}
+
+	@Override
+	public void startElement(String uri, String localName, String qName, Attributes attributes) 
+		throws SAXException 
+	{
+		if (localName.equalsIgnoreCase(lockon_element_)) {
+			hashmap_ = new HashMap<String,String>();
+			locked_on_ = true; // => covert each child element inside this to a hashmap key
+		}
+		else if (locked_on_) {
+			active_element_ = localName.toLowerCase();
+			inner_text_ = new StringBuffer();
+		}
+		// else ignore element as outside area of interest
+	}
+
+	@Override
+	public void endElement(String uri, String localName, String qName) 
+		throws SAXException 
+	{
+		if (localName.equalsIgnoreCase(lockon_element_)) {
+			// push hashmap onto arraylist
+			lockon_list_.add(hashmap_);
+			hashmap_ = null;
+			
+			locked_on_ = false;
+		}
+		else if (localName.equalsIgnoreCase(active_element_))
+		{
+			// add 'inner_text_' into hashmap under the key 'active_element_';
+			hashmap_.put(active_element_,inner_text_.toString());
+			//Log.d("***", "Saving: " + active_element_+ " = " + inner_text_.toString() + "\n");
+			active_element_ = null;
+			inner_text_ = null;
+		}
+	}
+
+	@Override
+	public void characters(char[] ch, int start, int length)
+		throws SAXException 
+	{
+		if ((locked_on_) && (active_element_ != null)) {
+			String text = new String(ch,start,length);
+			inner_text_.append(text);
+		}
+	}
+
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedMarkerOverlay.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedMarkerOverlay.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedMarkerOverlay.java	(revision 24268)
@@ -0,0 +1,93 @@
+package org.mapsforge.android.maps;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Thread-safe implementation of the {@link ItemizedMarkerOverlay} class using an {@link ArrayList} as
+ * internal data structure.
+ */
+public class ArrayItemizedMarkerOverlay extends ItemizedMarkerOverlay<OverlayMarkerItem> {
+	private static final int ARRAY_LIST_INITIAL_CAPACITY = 8;
+	private static final String THREAD_NAME = "ArrayItemizedMarkerOverlay";
+
+	protected final Context context;
+	protected AlertDialog.Builder dialog;
+	protected OverlayItem item;
+	protected final ArrayList<OverlayMarkerItem> overlayMarkerItems;
+
+	/**
+	 * Constructs a new ArrayItemizedOverlay.
+	 * 
+	 * @param defaultMarker
+	 *            the default marker.
+	 * @param context
+	 *            the reference to the application context.
+	 */
+	public ArrayItemizedMarkerOverlay(Drawable defaultMarker, Context context) {
+		super(boundCenterBottom(defaultMarker));
+		this.context = context;
+		this.overlayMarkerItems = new ArrayList<OverlayMarkerItem>(ARRAY_LIST_INITIAL_CAPACITY);
+	}
+
+	/**
+	 * Adds the given item to the Overlay.
+	 * 
+	 * @param overlayItem
+	 *            the item that should be added to the Overlay.
+	 */
+	public synchronized void addOverlay(OverlayMarkerItem overlayItem) {
+		this.overlayMarkerItems.add(overlayItem);
+		populate();
+	}
+
+	/**
+	 * Adds all items of the given collection to the Overlay.
+	 * 
+	 * @param c
+	 *            collection whose items should be added to the Overlay.
+	 */
+	public synchronized void addOverlays(Collection<? extends OverlayMarkerItem> c) {
+		this.overlayMarkerItems.addAll(c);
+		populate();
+	}
+
+	/**
+	 * Removes all items from the Overlay.
+	 */
+	public synchronized void clear() {
+		this.overlayMarkerItems.clear();
+		populate();
+	}
+
+	@Override
+	public String getThreadName() {
+		return THREAD_NAME;
+	}
+
+	/**
+	 * Removes the given item from the Overlay.
+	 * 
+	 * @param overlayMarkerItem
+	 *            the item that should be removed from the Overlay.
+	 */
+	public synchronized void removeOverlay(OverlayMarkerItem overlayMarkerItem) {
+		this.overlayMarkerItems.remove(overlayMarkerItem);
+		populate();
+	}
+
+	@Override
+	public synchronized int size() {
+		return this.overlayMarkerItems.size();
+	}
+
+	@Override
+	public synchronized OverlayMarkerItem createItem(int i) {
+		return this.overlayMarkerItems.get(i);
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedOverlayTweaked.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedOverlayTweaked.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedOverlayTweaked.java	(revision 24268)
@@ -0,0 +1,104 @@
+package org.mapsforge.android.maps;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Thread-safe implementation of the {@link ItemizedOverlay} class using an {@link ArrayList} as
+ * internal data structure.
+ */
+public class ArrayItemizedOverlayTweaked extends ItemizedOverlay<OverlayItem> {
+	private static final int ARRAY_LIST_INITIAL_CAPACITY = 8;
+	private static final String THREAD_NAME = "ArrayItemizedOverlayTweaked";
+
+	protected final Context context;
+	protected AlertDialog.Builder dialog;
+	protected OverlayItem item;
+	protected final ArrayList<OverlayItem> overlayItems;
+
+	/**
+	 * Constructs a new ArrayItemizedOverlay.
+	 * 
+	 * @param defaultMarker
+	 *            the default marker.
+	 * @param context
+	 *            the reference to the application context.
+	 */
+	public ArrayItemizedOverlayTweaked(Drawable defaultMarker, Context context) {
+		super(boundCenterBottom(defaultMarker));
+		this.context = context;
+		this.overlayItems = new ArrayList<OverlayItem>(ARRAY_LIST_INITIAL_CAPACITY);
+	}
+
+	/**
+	 * Adds the given item to the Overlay.
+	 * 
+	 * @param overlayItem
+	 *            the item that should be added to the Overlay.
+	 */
+	public synchronized void addOverlay(OverlayItem overlayItem) {
+		this.overlayItems.add(overlayItem);
+		populate();
+	}
+
+	/**
+	 * Adds all items of the given collection to the Overlay.
+	 * 
+	 * @param c
+	 *            collection whose items should be added to the Overlay.
+	 */
+	public synchronized void addOverlays(Collection<? extends OverlayItem> c) {
+		this.overlayItems.addAll(c);
+		populate();
+	}
+
+	/**
+	 * Removes all items from the Overlay.
+	 */
+	public synchronized void clear() {
+		this.overlayItems.clear();
+		populate();
+	}
+
+	@Override
+	public String getThreadName() {
+		return THREAD_NAME;
+	}
+
+	/**
+	 * Removes the given item from the Overlay.
+	 * 
+	 * @param overlayItem
+	 *            the item that should be removed from the Overlay.
+	 */
+	public synchronized void removeOverlay(OverlayItem overlayItem) {
+		this.overlayItems.remove(overlayItem);
+		populate();
+		super.requestRedraw();
+	}
+
+	@Override
+	public synchronized int size() {
+		return this.overlayItems.size();
+	}
+
+	@Override
+	public synchronized OverlayItem createItem(int i) {
+		return this.overlayItems.get(i);
+	}
+
+	@Override
+	protected synchronized boolean onTap(int index) {
+		this.item = this.overlayItems.get(index);
+		this.dialog = new AlertDialog.Builder(this.context);
+		this.dialog.setTitle(this.item.getTitle());
+		this.dialog.setMessage(this.item.getSnippet());
+		this.dialog.show();
+		return true;
+	}
+
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedTextAudioOverlay.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedTextAudioOverlay.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ArrayItemizedTextAudioOverlay.java	(revision 24268)
@@ -0,0 +1,45 @@
+package org.mapsforge.android.maps;
+
+import org.greenstone.android.tipple.TippleActivity;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.speech.tts.TextToSpeech;
+
+public class ArrayItemizedTextAudioOverlay extends ArrayItemizedOverlayTweaked
+{
+	protected final TextToSpeech tts_;
+	protected AlertDialog dialogAlert;
+	
+	public ArrayItemizedTextAudioOverlay(Drawable defaultMarker, Context context, TextToSpeech tts)
+	{
+		super(defaultMarker,context);
+		tts_ = tts;
+	}
+
+	@Override
+	public synchronized boolean onTap(int index) {
+		this.item = this.overlayItems.get(index);
+		this.dialog = new AlertDialog.Builder(this.context);
+		this.dialog.setTitle(this.item.getTitle());
+		this.dialog.setMessage(this.item.getSnippet());
+		dialogAlert = this.dialog.show();
+			
+		TippleActivity.log.optMessage("Manual Location Tap", "Title: " + item.getTitle());
+		
+		//OverlayTextAudioItem ta_item = (OverlayTextAudioItem)this.overlayItems.get(index);
+		//tts_.speak(ta_item.getTitle(), TextToSpeech.QUEUE_FLUSH, null);
+		//tts_.speak(ta_item.getTTSSnippet(), TextToSpeech.QUEUE_ADD, null);
+	
+		return true;
+	}
+	
+	public synchronized boolean unTap() {
+		
+		dialogAlert.cancel();
+		tts_.stop();
+		return true;
+	}
+}
+
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/CirclesOverlay.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/CirclesOverlay.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/CirclesOverlay.java	(revision 24268)
@@ -0,0 +1,137 @@
+package org.mapsforge.android.maps;
+
+import java.util.ArrayList;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+
+
+/**
+ * CirclesOverlay is a special Overlay to display a series of circles on top of the map. The radius of a
+ * circle is specified in meters and will be automatically converted to pixels at each redraw.
+ * <p>
+ * All rendering parameters like color, stroke width, pattern and transparency can be configured
+ * via the two {@link android.graphics.Paint Paint} objects in the
+ * {@link #CircleOverlay(Paint,Paint)} constructor.
+ */
+
+public class CirclesOverlay extends Overlay {
+	private static final String THREAD_NAME = "CirclesOverlay";
+	
+	private byte cachedZoomLevel;
+	
+	private Paint fillPaint;
+	private Paint outlinePaint;
+	
+	private final Path path;
+	private ArrayList<GeoPoint> center;
+	private ArrayList<Float> radius;
+	private ArrayList<Point> cachedCenterPosition;
+	
+	/**
+	 * Constructs a new CircleOverlay.
+	 * 
+	 * @param fillPaint
+	 *            the paint object which will be used to fill the overlay.
+	 * @param outlinePaint
+	 *            the paint object which will be used to draw the outline of the overlay.
+	 */
+	public CirclesOverlay(Paint fillPaint, Paint outlinePaint) 
+	{
+		this.path = new Path();
+		
+		this.center = new ArrayList<GeoPoint>();
+		this.radius = new ArrayList<Float>();
+		this.cachedCenterPosition = new ArrayList<Point>();
+		
+		setPaint(fillPaint, outlinePaint);
+	}
+
+	public synchronized void reset() {
+		this.center = new ArrayList<GeoPoint>();
+		this.radius = new ArrayList<Float>();
+		this.cachedCenterPosition = new ArrayList<Point>();
+		
+		path.reset();
+		super.requestRedraw();
+	}
+	
+	/**
+	 * Sets the parameters of the circle.
+	 * 
+	 * @param center
+	 *            the geographical coordinates of the centre point.
+	 * @param radius
+	 *            the radius of the circle in meters.
+	 */
+	public synchronized void addCircleData(GeoPoint center, float radius) {
+		this.center.add(center);
+		this.radius.add(radius);
+		this.cachedCenterPosition.add(new Point());
+		
+		this.cachedZoomLevel = Byte.MIN_VALUE;
+		super.requestRedraw();
+	}
+
+
+	/**
+	 * Sets the paint objects which will be used to draw the overlay.
+	 * 
+	 * @param fillPaint
+	 *            the paint object which will be used to fill the overlay.
+	 * @param outlinePaint
+	 *            the paint object which will be used to draw the outline of the overlay.
+	 */
+	public synchronized void setPaint(Paint fillPaint, Paint outlinePaint) {
+		this.fillPaint = fillPaint;
+		this.outlinePaint = outlinePaint;
+	}
+
+	@Override
+	protected synchronized void drawOverlayBitmap(Canvas canvas, Point drawPosition,
+			Projection projection, byte drawZoomLevel) {
+		if (this.center.size() == 0 || this.radius.size() == 0) {
+			// no valid parameters to draw the circle
+			return;
+		} else if (this.fillPaint == null && this.outlinePaint == null) {
+			// no paint to draw
+			return;
+		}
+
+		// make sure that the cached centre position is valid
+		if (drawZoomLevel != this.cachedZoomLevel) {
+			for (int i=0; i<center.size(); i++) {
+				Point cachedPoint
+					= projection.toPoint(this.center.get(i),this.cachedCenterPosition.get(i), drawZoomLevel);
+				this.cachedCenterPosition.set(i,cachedPoint);
+			}
+			
+			this.cachedZoomLevel = drawZoomLevel;
+		}
+
+		// assemble the path
+		this.path.reset();
+		for (int i=0; i<cachedCenterPosition.size(); i++) {
+			float radius_meters = projection.metersToPixels(this.radius.get(i), drawZoomLevel);
+					
+			this.path.addCircle(this.cachedCenterPosition.get(i).x - drawPosition.x,
+								this.cachedCenterPosition.get(i).y - drawPosition.y, 
+								radius_meters, Path.Direction.CCW);
+		}
+		
+		// draw the path on the canvas
+		if (this.fillPaint != null) {
+			canvas.drawPath(this.path, this.fillPaint);
+		}
+		if (this.outlinePaint != null) {
+			canvas.drawPath(this.path, this.outlinePaint);
+		}
+	}
+
+	@Override
+	protected String getThreadName() {
+		return THREAD_NAME;
+	}
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ItemizedMarkerOverlay.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ItemizedMarkerOverlay.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/ItemizedMarkerOverlay.java	(revision 24268)
@@ -0,0 +1,185 @@
+package org.mapsforge.android.maps;
+
+
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+/**
+ * ItemizedMarkerOverlay is an abstract base class to display a list of OverlayMarkerItems.
+ * 
+ * @param <Item>
+ *            the type of items handled by this Overlay.
+ */
+public abstract class ItemizedMarkerOverlay<Item extends OverlayMarkerItem> extends Overlay 
+{
+	private static final String THREAD_NAME = "ItemizedMarkerOverlay";
+
+	/**
+	 * Sets the bounds of the given drawable so that (0,0) is the center of the bottom row.
+	 * 
+	 * @param balloon
+	 *            the drawable whose bounds should be set.
+	 * @return the given drawable.
+	 */
+	protected static Drawable boundCenter(Drawable balloon) {
+		balloon.setBounds(balloon.getIntrinsicWidth() / -2, balloon.getIntrinsicHeight() / -2,
+				balloon.getIntrinsicWidth() / 2, balloon.getIntrinsicHeight() / 2);
+		return balloon;
+	}
+
+	/**
+	 * Sets the bounds of the given drawable so that (0,0) is the center of the bounding box.
+	 * 
+	 * @param balloon
+	 *            the drawable whose bounds should be set.
+	 * @return the given drawable.
+	 */
+	protected static Drawable boundCenterBottom(Drawable balloon) {
+		balloon.setBounds(balloon.getIntrinsicWidth() / -2, -balloon.getIntrinsicHeight(),
+				balloon.getIntrinsicWidth() / 2, 0);
+		return balloon;
+	}
+
+	private int bottom;
+	private final Drawable defaultMarker;
+	private Drawable itemMarker;
+	private final Point itemPosition;
+	private int left;
+	private Rect markerBounds;
+	private int numberOfItems;
+	private Item overlayItem;
+	private int right;
+	private int top;
+
+	/**
+	 * Constructs a new ItemizedOverlay.
+	 * 
+	 * @param defaultMarker
+	 *            the default marker for each item.
+	 */
+	public ItemizedMarkerOverlay(Drawable defaultMarker) {
+		this.defaultMarker = defaultMarker;
+		this.itemPosition = new Point();
+	}
+
+
+
+	/**
+	 * Returns the numbers of items in this Overlay.
+	 * 
+	 * @return the numbers of items in this Overlay.
+	 */
+	public abstract int size();
+
+	/**
+	 * Creates an item in the Overlay.
+	 * 
+	 * @param i
+	 *            the index of the item.
+	 * @return the item.
+	 */
+	protected abstract Item createItem(int i);
+
+	@Override
+	protected synchronized void drawOverlayBitmap(Canvas canvas, Point drawPosition,
+			Projection projection, byte drawZoomLevel) {
+		this.numberOfItems = size();
+		if (this.numberOfItems < 1) {
+			// no items to draw
+			return;
+		}
+
+		// draw the Overlay items
+		for (int i = 0; i < this.numberOfItems; ++i) {
+			// get the current item
+			this.overlayItem = createItem(i);
+
+			// check if the item has a position
+			if (this.overlayItem.getPoint() == null) {
+				continue;
+			}
+
+			// make sure that the cached item position is valid
+			if (drawZoomLevel != this.overlayItem.cachedZoomLevel) {
+				this.overlayItem.cachedMapPosition = projection.toPoint(this.overlayItem
+						.getPoint(), this.overlayItem.cachedMapPosition, drawZoomLevel);
+				this.overlayItem.cachedZoomLevel = drawZoomLevel;
+			}
+
+			// calculate the relative item position on the display
+			this.itemPosition.x = this.overlayItem.cachedMapPosition.x - drawPosition.x;
+			this.itemPosition.y = this.overlayItem.cachedMapPosition.y - drawPosition.y;
+
+			// get the correct marker for the item
+			if (this.overlayItem.getMarker() == null) {
+				this.itemMarker = this.defaultMarker;
+			} else {
+				this.itemMarker = this.overlayItem.getMarker();
+			}
+
+			// get the position of the marker
+			this.markerBounds = this.itemMarker.copyBounds();
+
+			// calculate the bounding box of the marker
+			this.left = this.itemPosition.x + this.markerBounds.left;
+			this.right = this.itemPosition.x + this.markerBounds.right;
+			this.top = this.itemPosition.y + this.markerBounds.top;
+			this.bottom = this.itemPosition.y + this.markerBounds.bottom;
+
+			// check if the bounding box of the marker intersects with the canvas
+			if (this.right >= 0 && this.left <= canvas.getWidth() && this.bottom >= 0
+					&& this.top <= canvas.getHeight()) {
+				// set the position of the marker
+				this.itemMarker.setBounds(this.left, this.top, this.right, this.bottom);
+
+				// draw the item marker on the canvas
+				this.itemMarker.draw(canvas);
+
+				// restore the position of the marker
+				this.itemMarker.setBounds(this.markerBounds);
+				
+				// Add text label next to marker
+				Paint paint_text = new Paint(Paint.ANTI_ALIAS_FLAG); 
+			
+				paint_text.setColor(Color.BLACK); 
+				paint_text.setStyle(Style.FILL);
+				paint_text.setTextSize(28); 
+				
+				canvas.drawText(overlayItem.getTitle(), itemPosition.x+16, itemPosition.y-23, paint_text);
+			}
+		}
+	}
+
+	@Override
+	protected String getThreadName() {
+		return THREAD_NAME;
+	}
+
+	/**
+	 * Handles a tap event.
+	 * <p>
+	 * The default implementation of this method does nothing and returns false.
+	 * 
+	 * @param index
+	 *            the position of the item.
+	 * 
+	 * @return true if the event was handled, false otherwise.
+	 */
+	//protected boolean onTap(int index) {
+	//	return false;
+	//}
+
+	/**
+	 * This method should be called after items have been added to the Overlay.
+	 */
+	protected final void populate() {
+		super.requestRedraw();
+	}
+
+}
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/OverlayMarkerItem.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/OverlayMarkerItem.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/OverlayMarkerItem.java	(revision 24268)
@@ -0,0 +1,97 @@
+package org.mapsforge.android.maps;
+
+
+
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+
+/**
+ * An OverlayMarkerItem holds all parameters of a single element on an {@link ItemizedMarkerOverlay}.
+ */
+public class OverlayMarkerItem 
+{
+	/**
+	 * Marker used to indicate the item.
+	 */
+	protected Drawable marker;
+
+	/**
+	 * Geographical position of the item.
+	 */
+	protected final GeoPoint point;
+
+	/**
+	 * Title of the item.
+	 */
+	protected final String title;
+	
+	/**
+	 * Cached position of the item on the map.
+	 */
+	Point cachedMapPosition;
+
+	/**
+	 * Zoom level of the cached map position.
+	 */
+	byte cachedZoomLevel;
+
+	/**
+	 * Constructs a new OverlayItem.
+	 * 
+	 * @param point
+	 *            the geographical position of the item.
+	 */
+	public OverlayMarkerItem(GeoPoint point, String title) 
+	{
+		this.point = point;
+		this.title = title;
+		this.cachedZoomLevel = Byte.MIN_VALUE;
+	}
+
+	/**
+	 * Returns the marker used to indicate the item (may be null).
+	 * 
+	 * @return the marker used to indicate the item.
+	 */
+	public Drawable getMarker() 
+	{
+		return this.marker;
+	}
+
+	/**
+	 * Returns the position of the item.
+	 * 
+	 * @return the position of the item.
+	 */
+	public GeoPoint getPoint() 
+	{
+		return this.point;
+	}
+
+	/**
+	 * Returns the title of the item.
+	 * 
+	 * @return the title of the item.
+	 */
+	public String getTitle() 
+	{
+		return this.title;
+	}
+	
+	/**
+	 * Sets the marker that is drawn on the map for this item. If the item marker is null, the
+	 * default marker will be drawn instead.
+	 * 
+	 * @param marker
+	 *            the marker that is drawn on the map for this item.
+	 */
+	public void setMarker(Drawable marker) 
+	{
+		this.marker = marker;
+	}
+}
+
+
+
+
+
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/OverlayTextAudioItem.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/OverlayTextAudioItem.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/OverlayTextAudioItem.java	(revision 24268)
@@ -0,0 +1,59 @@
+package org.mapsforge.android.maps;
+
+
+/**
+ * An OverlayTextAudioItem holds all parameters of a single element (capable of displaying text 
+ * and/or audio in {@link ItemizedOverlay}.  If the item has not raw audio file, then it will use
+ * Text-To-Speech (TTS) to synthesize the audio
+ */
+public class OverlayTextAudioItem extends OverlayItem
+{
+	protected final String ttsSnippet;
+	protected final String audioFile;
+	
+	/**
+	 * Constructs a new OverlayTextAduioItem.
+	 * 
+	 * @param point
+	 *            the geographical position of the item.
+	 * @param title
+	 *            the title of the item.
+	 * @param snippet
+	 *            the short description of the item.
+	 * @param tts_snippet
+	 *            optinal phonetically based version of the short description; can be null in
+	 *            which case the text in snippet is used.          
+	 * @param audio
+	 * 			  the audio file to use
+	 */
+	public OverlayTextAudioItem(GeoPoint point, String title, String snippet, String ttsSnippet,
+								String audio) 
+	{
+		super(point,title,snippet);
+		this.ttsSnippet = (ttsSnippet != null) ? ttsSnippet : snippet;
+		this.audioFile = audio;
+	}
+
+	/**
+	 * Constructs a new OverlayTextAudioItem.
+	 * 
+	 * @param point
+	 *            the geographical position of the item.
+	 * @param title
+	 *            the title of the item.
+	 * @param snippet
+	 *            the short description of the item.
+	 */
+	public OverlayTextAudioItem(GeoPoint point, String title, String snippet, String ttsSnippet) 
+	{
+		super(point,title,snippet);
+		this.ttsSnippet = (ttsSnippet != null) ? ttsSnippet : snippet;
+		this.audioFile = null;
+	}
+	
+	public String getTTSSnippet()
+	{
+		return ttsSnippet;
+	}
+}
+
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/RouteOverlayResetable.java
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/RouteOverlayResetable.java	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/src/org/mapsforge/android/maps/RouteOverlayResetable.java	(revision 24268)
@@ -0,0 +1,122 @@
+package org.mapsforge.android.maps;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+
+/**
+ * RouteOverlayResetable is a special Overlay to display a sequence of way nodes. To draw an arbitrary
+ * polygon, add a way node sequence where the first and the last way node are equal.
+ * <p>
+ * All rendering parameters like color, stroke width, pattern and transparency can be configured
+ * via the two {@link android.graphics.Paint Paint} objects in the
+ * {@link #RouteOverlay(Paint,Paint)} constructor.
+ */
+
+public class RouteOverlayResetable extends Overlay {
+
+	private static final String THREAD_NAME = "RouteOverlayResetable";
+
+	private Point[] cachedWayPositions;
+	private byte cachedZoomLevel;
+	private Paint fillPaint;
+	private Paint outlinePaint;
+	private final Path path;
+	private GeoPoint[] wayNodes;
+
+	/**
+	 * Constructs a new RouteOverlay.
+	 * 
+	 * @param fillPaint
+	 *            the paint object which will be used to fill the overlay.
+	 * @param outlinePaint
+	 *            the paint object which will be used to draw the outline of the overlay.
+	 */
+	public RouteOverlayResetable(Paint fillPaint, Paint outlinePaint) {
+		this.path = new Path();
+		this.cachedWayPositions = new Point[0];
+		setPaint(fillPaint, outlinePaint);
+	}
+
+	
+	public void reset() {
+		this.path.reset();
+		this.cachedWayPositions = new Point[0];
+		this.wayNodes = new GeoPoint[0];
+		super.requestRedraw();
+	}
+	
+
+	/**
+	 * Sets the paint objects which will be used to draw the overlay.
+	 * 
+	 * @param fillPaint
+	 *            the paint object which will be used to fill the overlay.
+	 * @param outlinePaint
+	 *            the paint object which will be used to draw the outline of the overlay.
+	 */
+	public synchronized void setPaint(Paint fillPaint, Paint outlinePaint) {
+		this.fillPaint = fillPaint;
+		this.outlinePaint = outlinePaint;
+	}
+
+	/**
+	 * Sets the way nodes of the route.
+	 * 
+	 * @param wayNodes
+	 *            the geographical coordinates of the way nodes.
+	 */
+	public synchronized void setRouteData(GeoPoint[] wayNodes) {
+		this.wayNodes = wayNodes;
+		if (this.wayNodes != null && this.wayNodes.length != this.cachedWayPositions.length) {
+			this.cachedWayPositions = new Point[this.wayNodes.length];
+		}
+		this.cachedZoomLevel = Byte.MIN_VALUE;
+		super.requestRedraw();
+	}
+
+	@Override
+	protected synchronized void drawOverlayBitmap(Canvas canvas, Point drawPosition,
+			Projection projection, byte drawZoomLevel) {
+		if (this.wayNodes == null || this.wayNodes.length < 1) {
+			// no way nodes to draw
+			return;
+		} else if (this.fillPaint == null && this.outlinePaint == null) {
+			// no paint to draw
+			return;
+		}
+
+		// make sure that the cached way node positions are valid
+		if (drawZoomLevel != this.cachedZoomLevel) {
+			for (int i = 0; i < this.cachedWayPositions.length; ++i) {
+				this.cachedWayPositions[i] = projection.toPoint(this.wayNodes[i],
+						this.cachedWayPositions[i], drawZoomLevel);
+			}
+			this.cachedZoomLevel = drawZoomLevel;
+		}
+
+		// assemble the path
+		this.path.reset();
+		this.path.moveTo(this.cachedWayPositions[0].x - drawPosition.x,
+				this.cachedWayPositions[0].y - drawPosition.y);
+		for (int i = 1; i < this.cachedWayPositions.length; ++i) {
+			this.path.lineTo(this.cachedWayPositions[i].x - drawPosition.x,
+					this.cachedWayPositions[i].y - drawPosition.y);
+		}
+
+		// draw the path on the canvas
+		if (this.fillPaint != null) {
+			canvas.drawPath(this.path, this.fillPaint);
+		}
+		if (this.outlinePaint != null) {
+			canvas.drawPath(this.path, this.outlinePaint);
+		}
+	}
+
+	@Override
+	protected String getThreadName() {
+		return THREAD_NAME;
+	}
+}
+
Index: /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/tipple-store/geodata/hamilton.loc
===================================================================
--- /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/tipple-store/geodata/hamilton.loc	(revision 24268)
+++ /other-projects/tipple-android/trunk-restructured/workspace/tipple-lib/tipple-store/geodata/hamilton.loc	(revision 24268)
@@ -0,0 +1,550 @@
+<?xml version="1.0" encoding="UTF-8" ?> 
+<tour>
+
+<location>
+  <latitude>-37.80608675</latitude>
+  <longitude>175.3029395</longitude>
+  <radius>0.01</radius>
+  <title>Beginning of gardens</title>
+  <text>
+    Welcome to the Hamilton Gardens. Hamilton Gardens is not a
+    traditional botanic garden. We like to say, that while botanic
+    gardens are collections of plants, Hamilton Gardens is a
+    collection of gardens. So we have taken lots of different kinds of
+    garden designs and collected them together in one place. We have
+    organized those gardens into five collections: Paradise Garden
+    collection, The Productive Garden collection, Fantasy garden
+    collection, Cultivar Garden collection and a landscape garden
+    collection. Hamilton Gardens itself started in 1964 and was
+    developed in kind of a hodgepodge sort of way until the 1980s when
+    a new overall plan was laid out.
+  </text>
+</location>
+
+<location>
+  <latitude>-37.806534</latitude>
+  <longitude>175.30264764</longitude>
+  <radius>0.01</radius>
+  <title>Beginning of paradise garden</title>
+  <text>
+    You are now at the paradise gardens. The reason why they're called
+    paradise gardens is because they all follow idea that a garden is
+    a kind of a paradise, so it's a refuge or retreat from the
+    everyday world. So they're all small walled gardens, enclosed
+    gardens. The word paradise comes from an old Persian word that
+    means enclosed garden. We have brought together here different
+    designs from different eras and different places around the
+    world. The paradise gardens are the most well known ones of all
+    our garden collections. We have used actual classic garden designs
+    that have been highly influential on gardens.
+  </text>
+</location>
+<location>
+  <latitude>-37.806782</latitude>
+  <longitude>175.3030759</longitude>
+  <radius>0.007</radius>
+  <title>Egyptian Court</title>
+  <text>
+    These are two statues that were first erected when the area was developed/ 
+    They're Egyptian to recognize the origin
+    of gardening in the Egyptian Nile Delta.
+  </text>
+</location>
+
+<location>
+  <latitude>-37.8066662</latitude>
+  <longitude>175.30305895</longitude>
+  <radius>0.005</radius>
+  <title>Japanese Garden</title>
+  <text>
+This is a Japanese Garden of Contemplation. There are certain key elements to look out for here, such as the kind of paradise element; this is a miniaturised, a very abstract version of nature. So everything here is very tightly controlled, and yet, represents something in nature. So some people like to think of this as a miniature landscape. You might say that the moss here is like fields, you know like we're flying across the fields. 
+The other thing to look out for in the Japanese Garden of Contemplation is the rocks and the rock placement. Have a look at the three rocks. When you look at them they don't look like much special but they are a classic kind of Confucian arrangement; where the two little rocks are bowing to the big rock there, which tells you something about respect for authority and respect to your elders. So there's coded messages in the garden, which it gives you a lot of pleasure and is fun to try and deconstruct or decode.
+
+The Japanese garden tries to create something like an abstract version of nature. For example, if  look at the trees. See how the branches are all trained to be straight out there and the trunk is curved; you look at very, very old pine trees they actually do that by themselves. But this is a very young pine tree but it's trained to look old. We don't want something that's a hundred feet tall because that would be out of scale with the garden. So Japanese gardens are a bit like Bonsai: it's trained to look very, very old and very big, but actually they're quite young and they're quite small.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8065514565</latitude>
+  <longitude>175.3029906750</longitude>
+  <radius>0.014</radius>
+  <title>Japanese Rock Garden</title>
+  <text>
+A Japanese garden is not all revealed to you in one grand moment. It's sort of revealed to you in stages. This part is often called the Zen garden. Its proper name is karesansui which means dry landscape or water mountain landscape. Again, obviously the rock placement is crucial. It's not something that we're necessarily trained to see. There are never any flowers in here; they're always pruned off. The classic kind of interpretation of this landscape is that it is a shoreline. So the gravel is the water and the swirling patterns of the current and then we have the headland from the islands and so on. 
+
+There are no flowers as it is thought that bright colours will disturb the tranquillity of the views. It's supposed to be very calming. The background is left blank; so it's as if they're drawn on a blank sheet of paper, and they are a bit like the landscapes that are drawn on silk screen scrolls. They use a lot of negative space when they're painting. 
+
+A similar effect can be seen when looking at the water. The rocks up close to us a really big and all the rocks on the far shore are kind of small, which it accentuates the distance.  So again it's a vast landscape in miniature, collected here for us. The Japanese garden designers take a lot of care to replicate the patterns in nature; the way that the water arose, the land and the way that the trees grow over the water. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8066596</latitude>
+  <longitude>175.3033258</longitude>
+  <radius>0.007</radius>
+  <title>Entrance to English Garden</title>
+  <text>
+Now we jump fast forward into the late 1800's; maybe early 20th Century and into England. There was a kind of back to nature type drive happening in response to all the industrialisation and things that was happening in England at the time. 
+What we've done here is we've taken three classic kind of garden designs, all based on layouts that were done by Gertrude Jekyll and her friend Lutyen. There is a long border, the collector's  garden and a white garden.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8065417</latitude>
+  <longitude>175.3033681</longitude>
+  <radius>0.005</radius>
+  <title>Long Border</title>
+  <text>
+The long border is just simply what it says, it's just a big long path with flowers either side; mostly annuals and some perennials and mixed border, as they say. The warm colours are in the middle and the cool colours are at the end. It has the reds and the oranges and the yellows around the middle, and then at the end all the blues and the more pastely, pinky, bluey things. It's actually quite carefully put together. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.806508</latitude>
+  <longitude>175.30349805</longitude>
+  <radius>0.007</radius>
+  <title>Collector's Garden</title>
+  <text>
+This is an English collector's garden. This is the garden that the plant collectors really like. By this stage in European garden history, lots and lots of plants have been collected from all around the world. People went to all sorts of interesting places and bring back all these plants, and growing them in their gardens. So, while the other kind of garden traditions are based a lot on what was available natively in that location at that time; this is one of those gardens that brings lots of interesting planting material together. This is a copy of a pavilion that exists that Millmead house. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80661782</latitude>
+  <longitude>175.3035174</longitude>
+  <radius>0.007</radius>
+  <title>White Garden</title>
+  <text>
+There's not just white flowers in here; there is also silver foliage like one the big trees. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.806797</latitude>
+  <longitude>175.3035393</longitude>
+  <radius>0.004</radius>
+  <title>Entrance to Chinese Garden</title>
+  <text>
+This is the Chinese Scholar's garden. This is the oldest one from all the paradise gardens. They go right back to second century if we like; although lots of these design elements were common to lots of different viewers. This garden tells the story of a life cycle. Along with the Japanese garden tradition, the Chinese gardeners collected rocks. And one of the things that the rocks connects to is those Chinese scrolls and the scroll art, and those paintings they would do of those amazing kind of mythical mountain ranges where the immortals were supposed to dwell. So this has been a miniature. Again we've got these big blank walls there with the rocks against them; so the classic opening. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8067807</latitude>
+  <longitude>175.3039754</longitude>
+  <radius>0.005</radius>
+  <title>The Entrance</title>
+  <text>
+One of the interesting things about this garden is the contrast, so this is the dark area; its covered in Jasmine and it smells really good when it's in flower. So there's dark and then there's light inside and outside and so on. So in terms of that story of that lifecycle, I guess that's kind of birth. 
+Again, there's a kind of representation of nature here that much less restrained and much less abstract  than the Japanese version. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80675698</latitude>
+  <longitude>175.3039148</longitude>
+  <radius>0.004</radius>
+  <title>Tree, Rock and Bridge</title>
+  <text>
+The rock in the enclosure there came from the bottom of Lake Taihu in China. It got shipped over here. The gardeners putt a different bonsai three there every month.
+	The bridge is not straight to stop the dragons coming across it. They're also dragon shapes represented on the top of those walls. In this garden you can see plants native to china. For the Chinese certain plants had strong associations. For example, the bamboo represented uprightness and strength. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8066641092</latitude>
+  <longitude>175.3039830923</longitude>
+  <radius>0.005</radius>
+  <title>Monk in Grotto</title>
+  <text>
+The cave holds a littel statue of a monk.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80680524</latitude>
+  <longitude>175.30421622</longitude>
+  <radius>0.005</radius>
+  <title>The High Point</title>
+  <text>
+This point symbolises the high point of your life; you get to this point and then you stand up here and you survey where you've been. It is a restful garden because there is a lot of green and not many flowers, just some shade. And at the end of the path, you find yourself back where you began.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80686102</latitude>
+  <longitude>175.30338661</longitude>
+  <radius>0.005</radius>
+  <title>Entrance to Modernist Garden</title>
+  <text>
+This is the modernist garden based on the designs of Thomas Church; most of his famous designs are in California. This garden represents a domestic backyard. All these plants here are specifically native plants. In America, gardens use American trees and American plants; the ones we used were mostly from Southern California. Some of them come from the east coast but most of them come from the west coast. 
+
+The design is very modernist; there's no symmetry and everything's kind of curvy. All the materials are kind of space age materials of the time. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80699413</latitude>
+  <longitude>175.30360142</longitude>
+  <radius>0.007</radius>
+  <title>American House</title>
+  <text>
+The intention when they built this garden was to symbolize the house with ranch sliders and one looks onto the back yard. Of course we can't build a whole house there so we just had a draft of it.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80688375</latitude>
+  <longitude>175.30317334</longitude>
+  <radius>0.005</radius>
+  <title>Indian Garden</title>
+  <text>
+
+The story behind this Indian garden is that we call it the Indian Shaba garden but it's really Persian. So the Persians came from Mongolia; conquered a whole lot of Asia and Western Europe and then settled down in Persia. After a few generations the Persians went eastwards and conquered a lot of northern Indian. They had come from a really dry, arid mountainous region; so for them, water and irrigation was really, really important, and you didn't waste it on big massive fountains. It was going to be really subtle, and it was going to serve the purpose of irrigation as well. By the time they had conquered India, the terrain was a lot flatter and water was a lot more plentiful, but they retained these kind of subtle elements. So there are irrigation channels and little bubbly fountains and water rills. So it's a classic case of something that started off as a design necessity; A couple of centuries later it was like a design exhibit; it's no longer necessary but people use it anyway.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80701817</latitude>
+  <longitude>175.30314905</longitude>
+  <radius>0.015</radius>
+  <title>Flower Beds</title>
+  <text>
+These four gardens get completely dug up twice a year, replace it with all new plants.  The gardeners don't use a lot of sprays but are very, very careful in their plant selections. They use a lot of original species rather than hybrids. Changing all the plants takes them a whole week, the entire workforce of Hamilton Gardens. We close it off and they just distribute them out and replant it. Because the garden designers were Muslims and had a restriction on representations of a human and animal figures, the designs that you see are often abstracts of plants.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8071147203</latitude>
+  <longitude>175.3031462431</longitude>
+  <radius>0.010</radius>
+  <title>River View</title>
+  <text>
+The director of Hamilton Gardens, Peter Sergel, who designed these gardens; took a trip to India and took photos of fountains that look exactly like that. He brought the photos home and he had guys reproduce them like that, but they're very close to the original thing. These are kinds of gardens that would have been on the banks of the river.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80681071</latitude>
+  <longitude>175.30282743</longitude>
+  <radius>0.012</radius>
+  <title>Italian Renaissance Garden</title>
+  <text>
+This is the Italian Renaissance garden; so obviously it comes from the Renaissance period, a rebirth of culture, coming out of the dark ages in Europe, and specifically in Italy where it starts in Florence. So you had a coming together of lots of different historical forces. 
+
+You had this concentration of extreme wealth firstly, and that was partially because of the Catholic church had its headquarters, and it was taking a lot of money from the rest of Europe. You had an increased scientific knowledge and increased humanistic rationalism coming along, and with that there was a huge opening up of trades. 
+
+This is quite a big garden by our standards. It is based on this small private side garden of a much, much bigger garden complex in Italy. The Italian merchants were quite wealthy and they spent money on their gardens. At the same time there's an increased interest in antiquity; so part of their Roman heritage and kind of the Roman Empire and so on. So there's a really interesting congruent here between a new rationalistic, scientific view of the world and a Christian catholic view of the world, and a pagan classical view of the world; all that is coming together there. For example, the water feature is a pagan Romulus and Remus statue. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8068357706</latitude>
+  <longitude>175.3027600050</longitude>
+  <radius>0.004</radius>
+  <title>Overview of Italian Garden</title>
+  <text>
+There's two kinds of key overall layout aspects. One is just the fact that there are three areas. So there's the outside bosco area beyond the garden in the forest, the untamed wilderness from which only the beasts live, and humans came from there but we don't live there anymore. The second area is the  orchards, the prater with its fruit trees and grapevines, and then the third one is the bottom part, which is the formal part of the garden. 
+
+Cicero talks about the fear of nature. There is the first nature, which is the untamed wilderness. The second nature, which is the farming, and then the third nature which is the garden. Third nature is nature plus art; whereas farming and gardening for food is nature plus science or functional behaviour. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80698196</latitude>
+  <longitude>175.30278038</longitude>
+  <radius>0.005</radius>
+  <title>Water in the Italian Garden</title>
+  <text>
+Another layout aspect is the progression of water. There are actually little nozzles in the wall next to the grotto and they make this lovely little mist and this is a grotto that represents the female and fertile. Beyond that there's some little fountains that go down underneath it and then there's the big fountain in the middle; it spurts up, and it's a much more masculine. And then beyond that there's the mighty river. 
+
+Obviously, it is a highly symmetrical garden area. You may notice that from each garden you can't see any of the other gardens. This garden and the Indian garden are really good examples of what garden designers do; which is that they borrow scenery, they borrow the landscape from outside. So, if your neighbour has a really big, lovely oak tree; you can build that into your design. And so here's a great example. The river is not part of Hamilton gardens but it certainly makes a good impact when you come out here and see it. 
+
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80704606</latitude>
+  <longitude>175.3024579</longitude>
+  <radius>0.006</radius>
+  <title>Medici Court</title>
+  <text>
+This is the Medici court; fantastic for outdoor theatre and so on, and the Medici gallery out there which is a little patio area.   </text>
+  <audio>
+  </audio>
+</location>
+
+<!-- maori gardens -->
+
+<location>
+  <latitude>-37.8068084</latitude>
+  <longitude>175.30235491</longitude>
+  <radius>0.005</radius>
+  <title>Tainui Landing</title>
+  <text>
+This is our newest garden and its consequently the one that we feel most proud of. It's called Te Parapara. Te Parapara is a traditional Maori garden of a kind. It's not a recreation of existing or historical Maori gardens as much as the Paradise gardens are, but it is much more of a narrative garden. It basically tells a story of the establishment of cultivated food crops in New Zealand. 
+
+The story begins at the gate; which represents the landing of Tainui waka in New Zealand in a kind of landfall. On the right hand side after the gate there is a pomaderris Tainui tree, big and tall. On the left is a little Pohutekawa tree. The Pohutukawa represents the tree that the Tainui waka was tied to when it first was landed at Tawhia. The Pomaderris represents the floor boards, and in the story of the landing of Tainui, the floor boards took root. We know that this is mythical because pomaderris was native. 
+  </text>
+
+<tts>
+This is our newest garden and its consequently the one that we feel most proud of. It's called Tee Paaraa-paaraa. Tee Paaraa-paaraa is a traditional Maori garden of a kind. It's not a recreation of existing or historical Maori gardens as much as the Paradise gardens are, but it is much more of a narrative garden. It basically tells a story of the establishment of cultivated food crops in New Zealand. 
+
+The story begins at the gate; which represents the landing of Tye-newee waakaa in New Zealand in a kind of landfall. On the right hand side after the gate there is a Poh-maadeerrees Tye-newee tree, big and tall. On the left is a little Poh-hootee-kawaa tree. The Poh-hootee-kawaa represents the tree that the Tye-newee waakaa was tied to when it first was landed at Kefeea. The Poh-maadeerrees represents the floor boards, and in the story of the landing of Tye-newee, the floor boards took root. We know that this is mythical because Poh-maadeerrees was native. 
+
+</tts>
+
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80666313</latitude>
+  <longitude>175.30244908</longitude>
+  <radius>0.005</radius>
+  <title>Hoturoa Statue</title>
+  <text>
+The statue here is Hoturoa and he was the captain of the Tainui waka. He is carved in a Tahitian style to represent the fact that there was no kind of indigenous New Zealand art; they were all from the islands. And so all these plants are the plants that we here natively when the waka began arriving. Lots of these native plants were used as foods or textiles, or other kinds of useful things by the early Maori. They discovered that these plants had medicinal uses or you could eat them. 
+
+  </text>
+<tts>
+The statue here is Hotooro-aa and he was the captain of the Tye-newee waakaa. He is carved in a Tahitian style to represent the fact that there was no kind of indigenous New Zealand art; they were all from the islands. And so all these plants are the plants that we here natively when the waka began arriving. Lots of these native plants were used as foods or textiles, or other kinds of useful things by the early Maori. They discovered that these plants had medicinal uses or you could eat them. 
+
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80704729</latitude>
+  <longitude>175.3022264</longitude>
+  <radius>0.009</radius>
+  <title>Gate</title>
+  <text>
+This structure represents an entrance way to a Pa or a Marae. The hut on the left of the entrance is a koutou which is a traditional food preparation structure and is outside because it's not tapu. Food preparation is not tapu; its noa, so it's not sacred. 
+
+The figures on the gate tell the story of the discovery of Oka, the use of ochre to preserve timber and preserve carvings. The ochre is made with a kind of iron rich clay, mineral rich clay that is often found in stream beds. There are lots of different colours: the red that you see most places to a yellow, a black and a white. It is mixed with fat which soaks into the timber and helps preserve the timber. Often times the coastal tribes would use shark fat. 
+
+Coming back to the figures on the gate, they tell the story of a man who was married and then the patupaiarehe, which are like the fairy people; came and stole his wife. He went to look for his wife and he couldn't ever find her because she would be there in the mist and then as soon as he went to get her she would disappear; so he could never get her back. So he went to see a Priest and the Priest said, &quot;You draw a circle around yourself with a mixture of kumara and ochre; smear it on the ground and then when she comes you'll embrace her and it will scare off the patupaiarehe&quot;. And the man did as he was told, and it worked. They say that the kind of the patupaiarehe who wanted to marry her still lives at the top of Mount Pirongia when the mist comes over that mountain. She is shown on the right hand side, her husband on the left side and the fairy people up the top. 
+
+The figures with square shaped faces represent Matariki, which is the Pleiades. This is the constellation that is quite important for timing the kumara harvest. For the Greeks it was seven sisters but for a Maori it's a woman and six daughters.
+  </text>
+<tts>
+This structure represents an entrance way to a Paa or a Ma-rye. The hut on the left of the entrance is a koutou which is a traditional food preparation structure and is outside because it's not tapoo. Food preparation is not tapoo; its noa, so it's not sacred. 
+
+The figures on the gate tell the story of the discovery of Oka, the use of ochre to preserve timber and preserve carvings. The ochre is made with a kind of iron rich clay, mineral rich clay that is often found in stream beds. There are lots of different colours: the red that you see most places to a yellow, a black and a white. It is mixed with fat which soaks into the timber and helps preserve the timber. Often times the coastal tribes would use shark fat. 
+
+Coming back to the figures on the gate, they tell the story of a man who was married and then the Paatoopai-aree, who are a kind of fairy people; came and stole his wife. He went to look for his wife and he couldn't ever find her because she would be there in the mist and then as soon as he went to get her she would disappear; so he could never get her back. So he went to see a Priest and the Priest said, &quot;You draw a circle around yourself with a mixture of koomera and ochre; smear it on the ground and then when she comes you'll embrace her and it will scare off the Paatoopai-aree. And so the man did as he was told, and it worked. They say that the kind of the Paatoopai-aree who wanted to marry her still lives at the top of Mount Peeron-keaa when the mist comes over that mountain. She is shown on the right hand side, her husband on the left side and the fairy people up the top. 
+
+The figures with square shaped faces represent Maataareekee, which is the Pleiades. This is the constellation that is quite important for timing the koomera harvest. For the Greeks it was seven sisters but for a Maori it's a woman and six daughters.
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80718379</latitude>
+  <longitude>175.30205507</longitude>
+  <radius>0.008</radius>
+  <title>Koomera</title>
+  <text>
+This is the realm of the cultivated food crops. They were brought here on the waka; they're not native to New Zealand, and there's actually just recently been a really important archaeological discovery on the west coast of the South American continent of a chicken bone that proves contact between Polynesians and south Americans. The Polynesians came to south America. When Columbus got to South America and discovered that there were chickens there. They didn't know why and at the same time Polynesians have got kumara, and kumara is native to south America. It doesn't appear in Europe; it's a new world crop. So there are theories that Polynesians came from Taiwan or they came from the old world. It leaves that question: Where did they get the kumara from? 
+
+The kumara we've got growing in this area was the most important crop for Maori. It was the  number one source of carbohydrate for them. All other the native plants together did not provide any productive capacity of this kind; the ease of its cultivation and so on. Kumara doesn't set seed in New Zealand because it is too cold. So every season they have to sort out the tubas that they're going to keep for the next year's planting and the tubas they're going to eat.
+
+	The maori accumulated a vast knowledge on this problem of how to make sure that they have got enough kumara for the next crop. When the European explorers got here they had this huge plantation of really well organized plantations of kumara. 
+
+So this entire garden is now set up for kumara planting in a central position. All kumara in this garden gets harvested and the first bit gets presented to the Maori King and then the rest of it gets eaten in a big hangi. The Hamilton Gardens grow at least two of the original four varieties from pre-European times. When Europeans got here and brought bigger, better kumara from South America, the Maori gardeners started using those as they yield bigger tubers. The old types of kumara got lost and then in the 1960s or 1970s the Crown science people were looking for the old varieties but could not find them anywhere. They had to go to a Japanese scientist who had been out here in New Zealand and got tubers to store them in Japan. So these old types that we have growing here owe their existence to a Japanese scientist.
+
+You will see that each plant is planted in a mound. The mound is there for lots of reasons. It increases the amount of sunlight that hits the ground, to keep it warmer for longer; especially when the sun gets low. You can't plant kumara until November and so it's really crucial to keep the end of the season as far away as possible. April, May; and the sun is getting quite low in the sky. Another aspect is increased drainage so the tubas don't rock. It also provides soil improvement and Maori gardening sites are usually  discovered by the existence of borough pits, basically big holes in the ground where good soil; which is usually very, very sandy; very pumicey, would be dug up and watered. So the soil you see here is full of rocks and pumice and sand to make it much better draining than it otherwise would be. So we find very advanced soil improvement techniques that are  certainly far beyond hunter gatherer type societies. 
+
+  </text>
+
+<tts>
+This is the realm of the cultivated food crops. They were brought here on the waakaa; they're not native to New Zealand, and there's actually just recently been a really important archaeological discovery on the west coast of the South American continent of a chicken bone that proves contact between Polynesians and south Americans. The Polynesians came to south America. When Columbus got to South America and discovered that there were chickens there. They didn't know why and at the same time Polynesians have got koomera, and koomera is native to south America. It doesn't appear in Europe; it's a new world crop. So there are theories that Polynesians came from Taiwan or they came from the old world. It leaves that question: Where did they get the koomera from? 
+
+The koomera we've got growing in this area was the most important crop for Maori. It was the  number one source of carbohydrate for them. All other the native plants together did not provide any productive capacity of this kind; the ease of its cultivation and so on. Koomera doesn't set seed in New Zealand because it is too cold. So every season they have to sort out the tubas that they're going to keep for the next year's planting and the tubas they're going to eat.
+
+	The maori accumulated a vast knowledge on this problem of how to make sure that they have got enough koomera for the next crop. When the European explorers got here they had this huge plantation of really well organized plantations of koomera. 
+
+So this entire garden is now set up for koomera planting in a central position. All koomera in this garden gets harvested and the first bit gets presented to the Maori King and then the rest of it gets eaten in a big hangi. The Hamilton Gardens grow at least two of the original four varieties from pre-European times. When Europeans got here and brought bigger, better koomera from South America, the Maori gardeners started using those as they yield bigger tubers. The old types of koomera got lost and then in the 1960s or 1970s the Crown science people were looking for the old varieties but could not find them anywhere. They had to go to a Japanese scientist who had been out here in New Zealand and got tubers to store them in Japan. So these old types that we have growing here owe their existence to a Japanese scientist.
+
+You will see that each plant is planted in a mound. The mound is there for lots of reasons. It increases the amount of sunlight that hits the ground, to keep it warmer for longer; especially when the sun gets low. You can't plant koomera until November and so it's really crucial to keep the end of the season as far away as possible. April, May; and the sun is getting quite low in the sky. Another aspect is increased drainage so the tubas don't rock. It also provides soil improvement and Maori gardening sites are usually  discovered by the existence of borough pits, basically big holes in the ground where good soil; which is usually very, very sandy; very pumicey, would be dug up and watered. So the soil you see here is full of rocks and pumice and sand to make it much better draining than it otherwise would be. So we find very advanced soil improvement techniques that are  certainly far beyond hunter gatherer type societies. 
+
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.80716643</latitude>
+  <longitude>175.30177131</longitude>
+  <radius>0.004</radius>
+  <title>Mulberry Tree</title>
+  <text>
+This is paper mulberry which can be used to make a type of cloth out of it. During the summer it grows like a weed. In the winter time it just dies down. In Northland it grows for the whole year but where there's a frost, it will die back. 
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8072314</latitude>
+  <longitude>175.30184732</longitude>
+  <radius>0.005</radius>
+  <title>Taro Plant</title>
+  <text>
+This one is a Taro plant. You can eat the leaves and its root.
+  </text>
+
+  <tts>
+This one is a Taaro plant. You can eat the leaves and its root.
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.8071683645</latitude>
+  <longitude>175.3020411730</longitude>
+  <radius>0.014</radius>
+  <title>Three Houses</title>
+  <text>
+
+You can see three structures here. They are really interesting and the main focus point. All three of them are storage buildings. When the European missionaries arrived, they reported that the most elaborate sanctuary buildings in the village were not the chief's houses; they were the store rooms. There's an entirely alternative approach to property compared to the European approach. The rua goes down underground so it keeps the kumara's cool and dry. The patika and the whatarangi are raised up, to give security from feeding kids and rats.
+
+Storage house design varied depending on the tribe... some reports would say a whatarangi was 10 metres in the air with one single pole, and it was for dead people; they would put skeletons up there. 
+If you look at the bottom panel of teh largest structure  - that's a replica of a piece of wood that was found buried in a garden in Chartwell in the 1970s; this original piece of wood now belongs to the Waikato museum. So every effort has been made in this garden to make sure that the carvings are accurate, to pre-European carving style. Because of course, like any art form, there's changes in style and changes in technique, so where practical they have used traditional stone tools and traditional patterns 
+
+  </text>
+<tts>
+
+You can see three structures here. They are really interesting and the main focus point. All three of them are storage buildings. When the European missionaries arrived, they reported that the most elaborate sanctuary buildings in the village were not the chief's houses; they were the store rooms. There's an entirely alternative approach to property compared to the European approach. The rua goes down underground so it keeps the koomera's cool and dry. The pateeke and the fataran-kee are raised up, to give security from feeding kids and rats.
+
+Storage house design varied depending on the tribe... some reports would say a fataran-kee was ten metres in the air with one single pole, and it was for dead people; they would put skeletons up there. 
+If you look at the bottom panel of the largest structure  - that's a replica of a piece of wood that was found buried in a garden in Chartwell in the 1970s; this original piece of wood now belongs to the Waikato museum. So every effort has been made in this garden to make sure that the carvings are accurate, to pre-European carving style. Because of course, like any art form, there's changes in style and changes in technique, so where practical they have used traditional stone tools and traditional patterns 
+
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.760980725</latitude>
+  <longitude>175.308644771</longitude>
+  <radius>0.02</radius>
+  <title>17 Ernest Road</title>
+  <text>
+    Ah our home at last.  I'll just sit down and have a nice rest ...
+  </text>
+  <audio>
+  </audio>
+</location>
+
+
+<location>
+  <latitude>-37.76091</latitude>
+  <longitude>175.309101</longitude>
+  <radius>0.02</radius>
+  <title>21 Ernest Road</title>
+  <text>
+    The home of my friends Cara and Kaden.
+  </text>
+  <audio>
+  </audio>
+</location>
+
+<!-- Univeristy of Waikato -->
+
+
+<location>
+  <latitude>-37.788972</latitude>
+  <longitude>175.317668</longitude>
+  <radius>0.03</radius>
+  <title>G-Block</title>
+  <text>
+    This building houses the departments of Computer Science, Mathematics
+    and Statistics.
+
+    Here are some Maori names:
+      kumara, tapu, Waikato, Tainui, Patika, Whatarangi, Taro, Matariki, Pirongia, Patupairehe, Marae, Pa, Hoturoa, Te Parapara, Pohutekawa, Kawhia, Pomaderris
+  </text>
+  <tts>
+    This building houses the departments of Computer Science, Mathematics
+    and Statistics.
+
+    Here are some Maori names:
+      koomera, tapoo, Waikato, Tye-newee, Pateeke, Fataran-gee, Taaro, Maataareekee, Peeron-keaa, Paatoopai-areehee, Maarai, Paa, Hotooro-aa, Tee Paaraa-paaraa, Poh-hooteekawaa, Kefeea, Poh-maadeerrees
+  </tts>
+  <audio>
+  </audio>
+</location>
+
+<location>
+  <latitude>-37.78764</latitude>
+  <longitude>175.317732</longitude>
+  <radius>0.05</radius>
+  <title>The Library</title>
+  <text>
+  </text>
+  <audio>
+  </audio>
+</location>
+
+
+<!-- Empty space -->
+<!-- geo fix 175.308644771 -37.7605 -->
+
+<!-- 17 Ernest Road -->
+<!-- geo fix 175.308644771 -37.760980725 -->
+
+<!-- 21 Ernest Road -->
+<!-- geo fix 175.309101 -37.76091 -->
+
+<!-- Garden start -->
+<!-- geo fix 175.3029395 -37.80608675 -->
+
+    	
+</tour>
