Thursday, November 11, 2010

Getting ADB to work with the Samsung Tablet

It is a neat little device, the Samsung Galaxy Tablet. However, as soon as I got one, I wanted to hook it to my computer and run some of my code on it. Easy enough, right? Well, not exactly.

After fighting with it for several hours yesterday, I gave up and called it a day. This morning I posted to the Android developers list looking for help. Then, on a hunch, I installed the Samsung Galaxy S driver pack, and managed to make progress.

I downloaded the driver from here. Make sure that you click on the link that says "Softpedia Secure Download" as there seem to be a lot of ads on the page that try to get you to install other crap.

Once you have the driver downloaded, unzip it someplace. (I threw it on the desktop, because it was easy.) Then, open the control panel, go to System, then Device Manager, and find the SAMSUNG_Android device that should be listed in the "Unknown Devices" section. Right click the device, and select "Update Driver Software...".

Next, select the option to "Browse my computer for driver software", then enter the path to the "USB Drivers" directory you extracted from the download. Make sure that the "Include subfolders" option is checked, and click Next.

At this point, the main USB driver should be found. After waiting a second, you should see a couple of new USB devices show up in the "Unknown Devices" section. One will be a modem interface, the other is the ADB interface that we want to use.

If you follow the same procedure above on these new devices you should be able to get the correct drivers installed. Once all of the drivers are installed, you should be ready to go! Running "adb devices" should show your Samsung tablet.

Sunday, October 17, 2010

Fancy List Items in a ListView

A while ago, I was working on a project where I wanted to be able to have Android show a ListView, with custom entries in the list. In most other languages and toolkits, doing this is a bit of a pain. You have to implement a special painter to pain the list cell in just the right way. Unless you are a pretty die-hard developer for that toolkit you may find yourself horribly confused as to how to handle the situation.

Fortunately, Android made this REALLY easy. If you know how to do a layout for a form, you know almost everything you need to know in order to make a fancy list item.

First, you want to create a small layout that specifies how a single list entry should look. For my purposes, I wanted to have a single line of large text, with smaller text below. I also wanted the large text to be bright white, and the smaller text to be a darker gray color. Finally, the smaller text should be indented on both sides to indicate that it is subordinate to the larger text.

No problem! A basic linear layout will do the job nicely. My final layout looks like this :


<linearlayout android="http://schemas.android.com/apk/res/android" layout_height="wrap_content" layout_width="fill_parent" orientation="vertical">
<textview layout_height="wrap_content" id="@+id/LargeText" text="Large Text" textcolor="@color/white" textsize="25dip" layout_width="fill_parent"></textview>
<textview layout_height="wrap_content" layout_width="fill_parent" layout_marginright="10dip" layout_marginleft="10dip" id="@+id/SmallText" text="Smaller Text" textcolor="#c0c0c0"></textview>
</linearlayout>


Easy enough. Now that we have that, we need to actually write some code that will use it. I find it easiest to create a convenience class that lets me manipulate the list easily. So, I create a class called MultiLineList, that looks something like this :


import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.widget.SimpleAdapter;

public class MultiLineList {

private ArrayList<HashMap<String,String>> list = new ArrayList<HashMap<String,String>>();
private Activity parentActivity;
private SimpleAdapter taskAdapter;

public MultiLineList(Activity parent)
{
parentActivity = parent;

taskAdapter = new SimpleAdapter(parentActivity, list,
R.layout.multi_item_layout, new String[] { "largetext","smalltext" },
new int[] { R.id.LargeText, R.id.SmallText } );
}

public void clear()
{
list.clear();
}

public void addItem(String largeText, String smallText)
{
HashMap<String,String> item = new HashMap<String,String>();
item.put("largetext", largeText);
item.put("smalltext", smallText);
list.add(item);
}

public SimpleAdapter getData()
{
return taskAdapter;
}
}


In the constructor, we create a SimpleAdapter that we use to map our text in to the different rows of the list view we will show the user. "R.layout.multi_item_layout" is the name of the layout we created above. (You may need to change this if you called your layout something else.) The two strings, followed by the two ints give us a mapping in a hash table for how to identify the two different TextEdits that we put in the layout. There is nothing special about only having two items. We could easily have 10 strings and 10 matching ints. What is important is that each string has a corresponding int that maps to a text edit in your layout.

The addItem() method is where the good stuff happens. As part of creating the SimpleAdapter in the constructor, we told it to use the "list" variable to get its data out of. So, the addItem() method allows us to easily put new text in to the list to be used. So, calling addItem("I am big", "I am not") would give us a single entry in the list view that contained the text "I am big" in the large font, and "I am not" in the small font.

Now that we have all of the ground work in place, it is time to put the pieces together. Moving forward, I am going to assume that you have a basic form set up with a list view configured in it. Also, that a ListView variable points to it called "listView". From there, using our fancy list widgets would look something like this :


myList = new MultiLineList(this);
myList.addItem("Large 1", "Small 1");
myList.addItem("Large 2", "Small 2");

listView.setAdapter(myList.getData());



Now, the next time out list view is drawn, it will contain two items in the list. The first one will have "Large 1" in the larger text, and "Small 1" in the smaller text. The second one will contain "Large 2" in the larger text, and "Small 2" in the smaller text.

From this simple example, you should be able to easily expand your list items to contain any just about any widget type you would want. But, I leave that up to you to experiment with.

Saturday, August 7, 2010

Its all about the layers..

For an Android project I am currently working on, I wanted to take an image of the last state of my game board, and overlay another image on it. While this seems like a simple thing it wasn't terribly simple to figure out. (Though, it is surprisingly simple to implement.)

To make this easy, lets assume you have two bitmaps. We will call them bitmap1, and bitmap2. Also to make it easy, we will assume they are stored as drawables in your Eclipse project.

It is also important to note that bitmap2 must have some transparent areas on it. If bitmap2 is completely opaque, you won't see any of bitmap1, and this code will be a waste of effort. (Since you could just draw bitmap2 by itself.)

The first thing we want to do is get our drawables stored in Bitmap objects. These bitmap objects should be called bitmap1 and bitmap2. This is easily done with the following code :


BitmapDrawable draw1 = (BitmapDrawable)res.getDrawable(R.drawable.bitmap1);
Bitmap bitmap1 = draw1.getBitmap();

BitmapDrawable draw2 = (BitmapDrawable)res.getDrawable(R.drawable.bitmap2);
Bitmap bitmap2 = draw2.getBitmap();



That was easy. Now, we have to figure out how to combine the two. This is where things start to get a little less obvious. After Googling around, I found several different methods that could do it. But, the method I am going to use below seemed the most straightforward to me.

First, the code, then the explanation :



Bitmap layers = Bitmap.createBitmap(bitmap1.getWidth(), bitmap1.getHeight(), bitmap1.getConfig());
Canvas c = new Canvas(layers);
c.drawBitmap(bitmap1, new Matrix(), null);
c.drawBitmap(bitmap2, new Matrix(), null);



Believe it or not, you now have a merged bitmap in memory. (But how? It looks like all I did was draw on a canvas!?)

Simple, if you look at the Canvas class, you will find that one of the constructors allows you to pass in a Bitmap object. When you do this, anything that is drawn on the canvas is put in to the Bitmap that was passed in. So, in our example above, if we wanted to see the result, we could draw the Bitmap object 'layers' to the screen, and we would have the image of bitmap2 laid over bitmap1.

Or, to look at it another way. If you were working with a SurfaceView, and wanted to draw the bitmaps there, you would create a Canvas object, draw the two bitmaps in order, and pass the Canvas to the SurfaceView where it would be drawn. The code here does the same basic thing. However, instead of having the code drawn immediately on the SurfaceView, you have the output sent to a Bitmap.

But, lets look at some assumptions in this code real quick. When we create the 'layers' Bitmap object, we have to tell the Bitmap object a little bit about the bitmap we want to create. Specifically, we have to tell it what the size of the bitmap is, and what the configuration of the bitmap is. The size is fairly obvious. But, in the sample code above, we are assuming that either the two bitmaps are the same size, or that bitmap2 is smaller than bitmap1. If bitmap1 is actually smaller than bitmap2, then the final bitmap will be the same size as bitmap1, and bitmap2 will be clipped to fit. But what about the configuration? What is that? The configuration is a value that tells the bitmap what kind of data it will be storing. Or, another way to look at it, how big each pixel will be in memory. Without getting in to a bunch of detail, under normal circumstances, you will want to use ARGB_8888.

Why the heck would you ever do this? Well, if you needed a composite image that would be used repeatedly in your program you could either try to draw it in real-time every time it was needed, or draw it once and just reuse the drawing. If the image is more than a few pixels, recreating the overlay each time could be an expensive operation. Caching the result and using that instead can save you a fair bit of processing time that you can use for other cool effects in your program!

Monday, July 12, 2010

The right way to do databases in Android

Feel free to comment below on what a loser I am. I am sitting in a cabin outside of Yellowstone National Park, writing a blog entry about doing databases on Android. However, I do think I should get a little geek cred as I am posting it from an EDGE connection on T-Mobile across the FroYo wireless tether. But, anyway...

When I first started working with Android, I found a simple tutorial on how to build SQLite databases. A few weeks ago, I was trying to track down the information again for project for my day job. After searching a bit, I was unable to find the original tutorial, but I found one that was a little better. The newer tutorial pointed me toward a much better way to handle databases.

In the Android SDK is a class called SQLiteOpenHelper. This class has two methods that Eclipse will force you to implement. The first is onCreate(), the second is onUpdate().

The onCreate() method is automagically called when you instantiate your descendant class, and the target database doesn't exist. In this method, you should issue all of the necessary SQLite commands to create the tables in your database. But, hold on a second. How can you put tables in the database when you didn't specify the database name? Simple, when the class is instantiated, the database name is passed in. The SQLiteOpenHelper class then takes care of creating the database for you, before the onCreate() method is called.

The onUpdate() method is also really handy and cool. When you instantiate the descendant class, you also pass in a database version number. If the version number you pass in is higher than the version number when the database was created, the onUpdate() method is called with version number stored in the database, and the version number presented when the class is instantiated. onUpdate() is responsible for issuing the correct SQLite commands needed to bring the database up to the schema version specified when the class was instantiated.

Okay, so blah, blah, blah.. Sample code!?


public class DBOpenHelper extends SQLiteOpenHelper {

private final String MY_DATA_TABLE = "t_Data";
private final String MY_DATA2_TABLE = "t_Data2";

public DBOpenHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}

@Override
public void onCreate(SQLiteDatabase myDB) {

/* Create a Table in the Database. */
myDB.execSQL("CREATE TABLE IF NOT EXISTS "
+ MY_DATA_TABLE
+ " (mystring1 VARCHAR, mystring2 VARCHAR);");

myDB.execSQL("CREATE TABLE IF NOT EXISTS "
+ MY_DATA2_TABLE
+ " (mystring3 VARCHAR);");
}

@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// We don't use this just yet.
}

}




Great! So the database gets created, but how do we actually use it?

Well, I am glad you asked. The SQLiteOpenHelper has methods that will return a handle to the database in either read only, or read/write flavors. So, if we wanted to use our open helper to create the database, and add a record to the second table, it would look something like this :



public void addDbData()
{
DBOpenHelper dbHelper = new DBOpenHelper(this, "my_database", null, 1);
SQLiteDatabase myDb = dbHelper.getWriteableDatabase(); // Open for write.

// Now, use the normal SQLiteDatabase methods to work with the DB.
myDb.execSQL("insert into " + MY_DATA2_TABLE + " set mystring3='Foo!';");
}


After you call addDbData(), you will now have a new database created with the name 'my_database', with schema version 1. The MY_DATA2_TABLE will also contain a single record containing the string "Foo!".

Two things worth pointing out in the addDbData() method. First, if you only want to read data from your database, you can change "getWriteableDatabase()" to "getReadableDatabase()". Second, always be sure you close your database after you are finished with it! The Java garbage collector won't do it for you, and you will get leak errors sent to your logcat console.

I am not going to get in to the update method of the SQLiteOpenHelper. I leave that as an exercise for you.

Wednesday, June 9, 2010

Something simple. Get text from a ListView.

Perhaps this topic is considered too simple for most people, which is why there aren't more postings about it. But, it seems like a useful thing to know. Specifically, when you are using a ListView with the android.R.layout.simple_list_item_1 view, how the heck do you get the text from the item clicked in the OnItemClickListener class?

It is pretty simple. The second parameter of the onItemClick is a View that contains just the item that was clicked. (Or tapped, whatever.) If you used Eclipse to automagically generate the callback method for you, the variable will be named "arg1", which is what we will assume for this example.

Inside of the onItemClick method, you can figure out the text using the following code :

String s = ((TextView)arg1.findViewById(android.R.id.text1)).getText().toString();


Pretty simple, huh? But, the code doesn't quite explain itself. So, I'll take a stab at it.

A ListView is exactly what the name says. It is a list of views. When you create the ArrayAdapter that you will use to display the data, you also tell the ArrayAdapter what view it should use. In our case, we chose "android.R.layout.simple_list_item_1" so we could use a simple array for the data. What we are doing is referencing a very simple layout that only displays a single line of text. I am sure if you Google around you can find the XML definition.

Inside the layout is a single definition. It is a TextView named "text1". Since it is a built-in layout, we will find the layout information under "android.R", instead of just "R" where our layout definitions show up.

So, by executing findViewById() on the view that was clicked, and passed in as "arg1", we can get the text that the user clicked on.

Pretty simple huh?

Monday, May 31, 2010

Basic graphics with Android

I have been working on a small game, and come across a real lack of useful information on how to develop a non-real time game. (i.e. A game that is entirely event driven.)

I started with the usual route, attempting to use existing UI objects to create the look that I wanted. However, I was unable to get the look and behavior that I wanted. So, I looked closer at the Lunar Lander sample in the Android SDK. The problem is that the Lunar Lander sample was perfect if you wanted to develop a real-time game. But, I didn't.

So, what to do? After much Googling, I found suggestions that what I really wanted to do was create a specialized view that would contain my game board. Doing this turned out to be easier than I thought it would. Using the Eclipse wizards, I created a new class that would be my game board view. The class was derived from the View class. The simplistic class would look something like this :


package com.example.simplegame;

public class BoardView extends View {
public BoardView(Context context) {
super(context);

}

public BoardView(Context context, AttributeSet attrib)
{
super(context, attrib);

}
}


Now, let me save you some time and pain. When I started this effort, I used "super(context)" inside the second constructor. Doing that will prevent you from using findViewById() to get a pointer to the view in your form.

Okay, so now we have a basic class set up. But, it doesn't actually do anything useful. What we really wanted to do is have a small blue box drawn on our view. So how do we do that?

Simple. We add an onDraw() method to our class. onDraw() takes a single parameter, with is of type Canvas. The canvas is what we will draw on, and what will ultimately be shown on the screen. As previously stated, we want to draw a simple blue square on the screen, so our onDraw method should look something like this :


@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

Paint p = new Paint();
p.setAntiAlias(true);
p.setARGB(255, 0, 0, 255);

canvas.drawRect(0, 0, 10, 10, p);
}


Before we move on to talk about how to use our new View in a form, I want to point out a little bit more information on how onDraw() works. According to the Android documentation, you should assume that the canvas passed in to onDraw() is blank. (That is, you should never rely on anything you have previously drawn to still exist on the canvas.) Because of this, you will need to redraw your entire view each time onDraw() is called. In our sample, we draw the blue square each time onDraw() is called.

Now that we have a new view class set up, we need to figure out how to put it in a form, and use it. To make things easy, I first created a simple form with a generic View that fills the entire screen. It looks something like this :


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<View android:id="@+id/View01" android:layout_width="fill_parent" android:layout_height="fill_parent"></View>
</LinearLayout>


Now that we have our layout, we need to modify it to include our custom view. To do this, we change the <View> tags to be <com.example.simplegame.BoardView>. If you then take this class, and this form and wrap it in some boiler plate code that would display the form, you will get a form with a small blue square in the top left corner of the screen.

If you do some experimenting, you will find that you get the blue square on the screen even if you don't establish a pointer to the BoardView object in the form. While it may seem strange at first, if you think about it you will realize that all of the objects on a form exist as soon as the form is inflated. The findViewById() call is simply giving you a pointer to the object that already exists in memory. Therefore, the onDraw() method is called, even if your main program doesn't have a pointer to the view.

Wednesday, May 19, 2010

Meru configuration, part 1

As promised, I am going to give the QuickPhones phone a shot with the Meru gear. But first, I need to get the gear set up.

A quick rundown of what my configuration will look like :
1 - Meru MC 500 controller running version 3.4-103 software
1 - Meru MN-AP320
1 - Meru MN-AP201RH

I powered up the unit, and plugged in to it via a USB->Serial dongle. As the instructions indicated, I set up the serial port to 115200,8,N,1. I watched as the controller booted some flavor of Linux, and then hung on the "Starting Meru 3.4-103 wireless LAN services ...". After sitting for a while, I figured something was wrong, so I power cycled the box to see if it would come up a little quicker. (It didn't.)

I figured there was something wrong with my unit, so I pulled up a text editor to start writing my blog entry about how much I would have loved to write the entry about the QuickPhones with Meru. While I was getting everything together, the console spit out an error message, and then prompted me for a login.

I went ahead and put the default admin/admin username and password and got the console prompt back. At the console prompt, I ran the "setup" process. After answering the normal questions (ip address, gateway, netmask, etc.) the unit asked if it could reboot. (So I let it. Shocking, huh?)

Fortunately, it rebooted a lot faster than the initial boot. After a minute, I was able to log in and was back at the command prompt.

Normally, I am the kind of person that hates to configure gear with the web interface. But, in this case I decided to make an exception since the goal is not to evaluate the Meru gear, but rather to see if the phone works better on it. I fired up Firefox, and hit the web page. At this point, I was informed that I needed to use Internet Expoliter to access the UI on the box. (As an aside, I am sure that the version of code I am using is older. But, I still have to say, REALLY!? *sigh*)

Since I am already inflicting punishment on myself, I figured I would give IE 8 x64 a shot. When I hit the web site, I was first told that the certificate on the unit was suspicious (which is normal on gear like this), and after telling IE it was okay to continue, I was prompted for the username and password.

The UI that was displayed is reasonably clean, but doesn't show any useful information initially. (Probably because I don't have any APs it can talk to.) So, I pulled out the AP201RH. The first thing I noticed is that the AP appeared to want power over ethernet (PoE). So, I tried plugging it in to port 2 on the controller in hopes that it would power on. But, it seems that either port 2 doesn't do PoE, or there is some configuration setting that I need to enable before it will do it. Again, since I really want to get to the QuickPhones testing, I just took the AP in to another room that had a PoE port that I knew would work.

I managed to get the AP201RH up and running easily enough. So I plugged in the AP-320 so I could get it configured and ready to rock. Unfortunately, the AP-320 is running a different version of code, and try as I might, I am unable to upgrade, or downgrade it. Since the controller sees the AP as an AP-100, I am guessing this is a good thing.

Unfortunately, this is a dead end for this effort. I'll have to see if I can track down my friend at Meru to see if I can get some help. (The gear I have is test gear, so I don't have a maintenance contract to get the latest versions of code.)

I hope to have more in the coming weeks, assuming I can reconnect with an old friend.

Monday, May 17, 2010

Stupid Android ListView tricks

I recently made some major changes to the Movie Track app that I put out. What started as an app that I did for my own use seems to have gained a few users. So, I decided to upgrade the look and feel a bit. In the process of this, I changed several of the screens from using a ListActivity to using a normal XML layout that contained a ListView. After mucking around with all of the settings to get things looking the way I wanted, I ran in to an interesting snag.

Everything displayed the way I wanted it to, but to "click" on an item that was in the ListView, you had to actually "click" on the text of the ListView, and not just on the row in the ListView. Perhaps I am a perfectionist at some level, but this annoyed me to no end.

Much Google later, I couldn't find the answer on how to do it. So, I started to mess with settings. A hair-brained idea or two later, I decided to try changing the ListView's layout-width from "wrap_content" to "fill_parent". Amazingly enough, that did exactly what I wanted!

Sure, this was a bit of a "duh" experience, and a newbie mistake. But, I figured I would publish it here in case some other developer runs across the same thing, and gets as frustrated as I was getting!

Sunday, May 16, 2010

Adding the Mob to Android (Part 2)

So, if you followed my previous AdMob post, you should have your app listed in AdMob, have the SDK downloaded, and have the documentation available. If you don't, you should go look at previous articles in my blog to find the steps followed to get all of these things.

Since I rebuilt my development machine recently, the first step I took was to make sure the code for my app would still run in the emulator. This proved two things to me. 1) My development environment is solid and configured correctly. 2) My program is still working as I would expect it to. I did this because it is always a good idea to know that what you have works before you go sticking new and unknown stuff in to your app.

When I first loaded my project in to Eclipse, I got the error, "Android requires .class compatibility set to 5.0. Please fix project properties." As I recall, there were some requirements on the JDK that is used with Android, so I assume that this error message is a direct indication of what needs to be done to make sure my code still works. But, this error message is also not entirely clear. Have no fear, wizards are here! Right click on your project in Eclipse, then select "Android Tools > Fix Project Properties", and all of our problems are solved!

Now, it is time to get to the meat of the work. For the sake of brevity, I won't recreate all of the information contained in the SDK document you should have already downloaded. Instead, I will reference that document, and provide my own commentary on anything that I feel wasn't explained very well.

If we turn our SDK documents to page 2 (or, the first page following the title page), we see the first few steps to getting AdMob installed. My project didn't have the lib directory, so I went ahead and created it. Following the instructions in step 1 is pretty straightforward. However, I did run in to one thing that wasn't covered. When you create the lib directory, and copy the jar over you need to refresh your project, or else you won't be able to find the jar file you need to add. If you click on your project in Eclipse's package explorer, and hit the F5 key, you will see the lib directory added to your project. Now you should be able to follow the rest of the instructions in step 1.

For the most part, the rest of the instructions make sense. However, the final instruction that indicates how to get a test ad appears to have changed a little. The instructions indicate that you should look in the logcat output for a string that identifies your test device. It may be that this is what you need to do when debugging on an actual device, however, if you are just working in the emulator you only need to add the line "AdManager.setTestDevices(new String[] { AdManager.TEST_EMULATOR } );" to your onCreate() method.

If you followed all of the instructions carefully, you should now have an ad enabled version of your program! Now go make some money!

Saturday, May 15, 2010

Using Ubuntu 10.04 LTS server for Subversion

My old subversion server is in need of a bit of an upgrade. I decided to move it to one of my VMware ESXi servers. (Yes, I really have ESXi servers in my basement. What can I say?) Anyway, since this is a low volume service, moving to an ESXi server seems to be the most economical way to keep my code safe without running the power bill (any farther) through the roof.

Since I have enough VMs to keep track of, I am going to build this one on Ubuntu 10.04 LTS, since Ubuntu seems easier to keep up-to-date. However, taking you step-by-step through the installation isn't something I am going to do. Rather, I will comment on any "non-standard" steps I take, and then include all of the follow-up information needed to get the subversion server up and running.

When I installed the server, I didn't select any of the options on the type of server I wanted to set up. Instead, I just continued past that screen and completed the installation. Once the machine rebooted, I logged in with the account I created, and got to work.

The first two things you will probably want to do is set a static IP address for your server, and install the openssh server so you can work on the machine remotely. Setting the static IP is easy. Good instructions can be found here : http://www.howtogeek.com/howto/ubuntu/change-ubuntu-server-from-dhcp-to-a-static-ip-address/. Installing OpenSSH is also easy. At the server's command line run the command "sudo apt-get install openssh-server".

Once the IP address is set, you will either need to reboot the machine, or manually change the address by hand. I suggest a quick reboot, as it shouldn't take too long. Once the machine reboots, we can get on to setting up and configuring the subversion server.

There are several different ways to configure a subversion server. And I have used many of them. By far, my preferred method is to do subversion over http as the Tortoise SVN client for Windows will let me cache my username and password in this mode, and not have to enter it a bunch of times anytime I do any work in the repository.

So, the first thing we need to do is install Apache. I went ahead and installed it using the command "sudo apt-get install apache2". After a few seconds, I had Apache installed and ready to rock.

The next thing I needed to do was actually install subversion. This is easily done by running the command "sudo apt-get install subversion". After a few more seconds, I had subversion installed.

If we Google around, we can find some information on what to do next at https://help.ubuntu.com/community/Subversion. The only problem is, that I am using the server version, so the UI steps are of little help. But no problem, we can easily do the same thing from the command line!

First, we want to create a subversion group to use. To do that, we need to run the command "sudo addgroup subversion". You should get a message indicating that the group was added, along with listing the GID (group ID) of the group that was just created.

To do that, run the command "sudo nano /etc/group", then find the group we just created, and add both "www-data" and your username to the end of the subversion group line.

Now, you should be a member of the subversion group, but you will need to log out and back in before the change will take affect. So do that now.

Next, we need to create the directories to store the files that we upload via subversion. To do that, execute the following commands :

cd /home
sudo mkdir svn
cd svn
sudo mkdir repo
sudo svnadmin create /home/svn/repo


Then, fix the permissions with these commands :

cd /home/svn
sudo chown -R www-data:subversion repo
sudo chmod -R g+rws repo

Now, to bring things full circle, we need to make sure we have the Apache 2 SVN libraries installed. To do that, run the command "sudo apt-get install libapache2-svn".

Now, we need to edit the Apache config so that it knows how to deal with the subversion requests. Change in to the directory /etc/apache2/mods-available, and run the command "sudo nano dav_svn.conf". Then, add the following block to the end of the file to enable subversion through Apache :


DAV svn
SVNPath /home/svn/repo
AuthType Basic
AuthName "Subversion Repository"
AuthUserFile /etc/subversion/passwd

Require valid-user




Whew! This is a lot of work. BUT, we are almost there! The last thing we need to do is set up authentication for the web interface so that not just anyone can muck around in our repository. Doing that is easy. Run the command "sudo htpasswd -c /etc/subversion/passwd " where is replaced with the user name you want to use to log in. To add additional users, use the same command line, but remove the "-c" option. (If you don't remove the -c option you will erase the first user you created!!!)

Congratulations! At this point, you should have a subversion server all set up! To access the server, you would use the URL http://
/svn .

What does it take to add the Mob to your existing Android applications?

When I get bored, I code. And when I get money hungry, I also code. I currently have one app out on the Android marketplace, that has been downloaded several times. In all honesty, I didn't expect the app to become the next big thing. I also wasn't sure it would be useful to people beyond myself. But, it seems that there are at least a few other people out there that believe my Movie Track app has some value.

Now, to be fair, I am all for free apps. I have done a lot of free open source development in my time, and have generally enjoyed it. But, I also like to get a little something extra out of my work when possible. So, I have decided I am going to try adding AdMob to my Movie Track program, and see if I can generate a few bucks along the way. (For those that hate ads, I'll also offer an ad-free version for $1.)

Signing up for AdMob is pretty easy. Just hop over to the AdMob web site, and create a new account. Once the account is created, you will get an e-mail with a link that needs to be clicked in order to verify the e-mail account is yours. But, once you have done that, it starts to get confusing in a hurry as to what to do next.

First, some terminology. When I first logged in with my shiny new account, there was a lot of talk about "Advertisers" and "Publishers". Since we are putting ads in to our programs, we are "Publishers". So, unless you want to advertise things in other people's programs, you should look for information that pertains to publishers.

Once you log in to the site, and click on the Marketplace link, the Campaigns tab shows information that is only relevant to advertisers. Or does it? Clicking on the "New Advertiser Training Videos" takes you to a page with videos about how to use AdMob to advertise new products and services. However, if you scroll down toward the bottom of the page, you will also find that there are videos for publishers down there as well.

A quick look at some of those links tell us that we need to create an Android App profile on the AdMob site in order to allow us to put ads in, and track the results. To do this, we need to hover over the "Sites & Apps" tab, and select the "Add Site/App" option from the drop-down menu. Next, click on the "Android App" icon, and scroll down to enter the rest of the information.

As you fill this form out, there is a section for the Android package URL. The help information that is available says it must be a mobile site, or the URL to your package in the market place. (The FAQ says the URL in the market place will be formatted as : market://details?id) However, digging around, it seems there should be a "=" between the "id" and the "". You can find more information at http://developer.android.com/guide/publishing/publishing.html.

This left me with a couple of questions that I will probably post more about later as I dig deeper. The first one is, "What is this URL used for?". And the second is, "What if I have not yet published the app? What do I put in there?".

Since this field isn't in bold, I am going to guess that it may not be important what goes in there. But, I like to be complete where possible, so I will be putting in the link to my app in the form of "market://details?id=info.geektaco.movietrack". For the category, I selected "Entertainment", and then I entered a description of the Movie Track program, and clicked the "Continue" button.

It would appear that I entered the correct information, as the next screen I was presented with is a link to download the "Install Code" to add to my program. I went ahead and grabbed the SDK, and a copy of the PDF version of the SDK documentation for later use, and then clicked "Go to Sites/Apps".

Now, we are getting somewhere. We should now see the dashboard for AdMob. In the dashboard is a link to update your payment information. That part seems pretty straight forward, so I won't go in to detail here.

And now, for the fun part! Let's add the ads to our program, and go make some money!!!

(Continued in later posts)

Friday, May 14, 2010

The final(?) word on the QuickPhones phone.

It has been a while, but a few weeks ago I go another update from the QuickPhones guys about the multiple AP issue that I ran in to. I was extremely disappointed to hear that they were unable to find a fix for the multiple AP issue, and have given up trying.

So, what can I say? I will still tell you that this phone has the best battery life of any of the lower priced wifi phones out there. (I have not had a chance to try the more expensive ones. Feel free to contact me if you want to send me one!) When I didn't have it associated to a network, and just sitting on my desk the battery lasted about a month. When connected to an AP, the battery lasted about a week.

Now, just to be clear, it is possible that this phone would work perfectly on wireless gear that "pretends" to be a single huge AP. The one that comes to mind is Meru networks. I have some Meru gear that I am going to install, and will post a follow-up on this phone once I get it installed.

I have, however, discovered a bit of a workaround to the multi-AP issue. If you happen to have a single AP around that can broadcast another SSID, you can still use the phone in an area with multiple APs on one SSID. To do this, you need to shut down all of the APs that are broadcasting the same SSID so that the phone can pick up the AP that is single.

That didn't make a lot of sense, so let me expand on it. Lets say you have 5 "brand X" APs, all broadcasting an SSID called "CorporateFoo". While those APs are all broadcasting the SSID, the phone will be unable to consistently see any SSIDs around you. So, to get the phone working, you need to run to your local electronics store and purchase an inexpensive "brand Y" AP. Set that AP up and name it something like "IWishQuickPhonesWorkedOnMultiAPNets". (yes, I know that is too long for an SSID.. work with me here.) Now, shut down all of the APs that are broadcasting the "CorporateFoo" SSID, and reboot your QuickPhone. When the phone comes back up, it should be able to see your single AP that is broadcasting the "IWishQuickPhonesWorkedOnMultiAPNets" SSID. Configure the phone to use this SSID, make sure it connects and can talk to your SIP server. Once you verify it is working, you can turn your other APs back on. Now that you have the configuration set in the phone, it should always talk to the single AP. When it is powered on, it might take a while to find the AP, but it should eventually connect to it.

So, in a nutshell, if battery life is king for you, and you either only have a single AP, or don't mind jumping through hoops to get your phone working, the QuickPhone is what you want. If having a phone off the charger and on standby for more than 24 hours isn't important, then I recommend the UniData phone I reviewed a few months ago. It is a solid phone that has been working perfectly in my environment.

Monday, May 3, 2010

Xfce and Ubuntu

The title may be a bit misleading. It is likely that this is really an Xfce issue only.

When I upgraded my Revo from Gentoo to Mythbuntu I ended up with a situation where the font size was significantly larger than it should have been. Okay, perhaps "significantly larger" would be an understatement. The clock on the menu bar was so large, I could only see the bottom lines for the numbers!

Googling around found that the issue was with Xfce trying to determine the best font size by using the DPI values from X. X, in turn, got the DPI information out of the EDID information from the monitor. I am sure in most cases this works just fine, but when you bring a cheap LCD flat panel TV in to the equation along with an ION graphics card, the results are less than stellar.

After searching many web pages looking for the answer, I finally found it in the first place I SHOULD have looked. (Yeah, I can admit when I was being stupid.) If you hop over to the MythTV Wiki at http://www.mythtv.org/wiki/Specifying_DPI_for_NVIDIA_Cards you can find the "magical" answer to the problem.

Specifically, you need to add the following options to the "Monitor" section :

Option "UseEdidDpi" "FALSE"
Option "DPI" "100 x 100"


After this, everything started to look reasonable on my screen again!

Sunday, May 2, 2010

Of Acer Aspire Revo and Ubuntu 10.04 LTS

I have been running one of my Myth frontends on an Acer Aspire Revo for a while now. When I first got it, I loaded it up with Gentoo, which was my favorite distro at the time. Since then, I have set up several other frontends using Mythbuntu, all of which speak to my backend still running on Gentoo.

Since all of my frontends except one were running Mythbuntu, I decided to do myself a favor, and convert the last one to Mythbuntu as I upgraded toward the final 0.23 release of Myth. It seemed like it would be a pretty straight forward thing to do, but it turns out, it isn't so straight forward.

I managed to get the system to install reasonably pain free. I selected my usual options during install, such as using the proprietary NVidia drivers, instead of the open source ones. (Mainly because I need VDPAU, or else these low power machines won't be able to keep up.) Following the final reboot, I saw some text on the screen about a failure to load something related to the NForce chipset, quickly followed by a blank screen. But, not just any blank screen, the blank screen my TV gives when there is either no signal, or an invalid signal on the input.

Needless to say, this was annoying. After running some nmap to chase down the DHCP address given to this machine, I was able to log in. While looking through a "ps xaf" output, I noticed that there was a process called "zenity" running, that seemed to be trying to display an error message about Ubuntu running in low graphics mode. Googling around found that this usually happens when the driver installed is incorrect, or improperly configured.

A quick "lsmod" showed that the expected nvidia kernel module wasn't running! At this point, it seemed pretty clear why the configuration didn't work! Looking through the available apt packages, I couldn't locate the proprietary nvidia drivers. (Or, at least they weren't where I expected them to be.)

A bit more Googling around came across this web site : http://www.ubuntugeek.com/howto-install-nvidia-drivers-manually-on-ubuntu-10-04-lucid-lynx.html

So, what does it all mean? Darn good question. I am far from an expert on the subject, but it would seem that the latest version of Ubuntu doesn't have a way to install the NVidia driver without going through the old method of downloading it directly from NVidia, and doing the work yourself.

What I can say, however, is once I followed the instructions from the link above, my machine booted right up in to X, and started working as I would have expected.

Wednesday, March 17, 2010

Marshal double dereferenced strings in C#

I have been working on some interesting bits and pieces of software that involve the use of C# on Windows Mobile. Specifically, I am using the DMProcessConfigXML() function call to feed an XML provisioning document in to Windows Mobile to change settings on the device.

Today I ran across the need to make full use of the function to query existing settings. To date, I had only pushed data in, I didn't have a need to actually read anything back out.

Where is where it gets fun. The data that is returned is defined as "LPWSTR* ppszwXMLout". Marshaling simple data types in C# isn't terribly hard, but a pointer to a pointer!? Such things are the crazy constructs of a C programmer! After digging around, I found a sample on jaredpar's blog that showed how to marshal a double dereferenced pointer to an integer. I figured getting a string would be similar, except the final step would be to marshal a string, instead of an int. Lucky for me, it really is that simple.

For more on the gory details, I would suggest clicking on the list to jaredpar's blog. He describes the details well, so I won't bother repeating his work. Instead, here is a quick bit of code that shows the difference for dealing with a string instead of an int :


public void queryProxyData()
{
IntPtr outptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
string query = "";

if (DMProcessConfigXML(query, 1, outptr) == 0)
{
// See if we can demarshal that crazy thing!
IntPtr newPtr = (IntPtr)Marshal.PtrToStructure(outptr, typeof(IntPtr));
string xmlData = Marshal.PtrToStringUni(newPtr);
Debug.WriteLine(xmlData);
Marshal.FreeHGlobal(newPtr);
}

Marshal.FreeHGlobal(outptr);
}


Pretty simple, huh? I can safely say this is a much better solution that a previous one that I found!

Friday, March 12, 2010

Well, isn't this QT... (Building 64-bit binaries on Windows 32-bit)

For a project that I work on, we have a 32-bit build "server" running on Windows XP Professional. We are starting to expand this project to support 64-bit systems. No problem, right? Visual Studio has cross-compilers for x64 machines. So, we can still use our 32-bit system to build 64-bit binaries.

Umm.. Sure... Something like that.. I spent a rather long weekend trying to figure out how to do this very thing. The problem that I ran in to is the UI for the project uses the QT libraries.

I started off trying to build the x64 QT libraries using the x64 compilers on a Windows XP development machine. I didn't get very far in the process before I hit a dead end. The build would bail out when it attempted to run qmake.exe. After beating my head on it for a bit, I realized that the build system that QT uses expects to compile tools like qmake, and then execute them to complete the rest of its build process. So, the build process would build qmake for the x64 architecture, then attempt to run it, which obviously wouldn't work. (Remember, I was on a 32 bit system.)

I suspect that someone that wanted to badly enough could work around this with various types of trickery. But, I happened to have an x64 machine kicking around my house, so I just built it on that machine, and then decided to try to move it to the 32-bit build system. The x64 build went smoothly when it was done on an x64 machine. (Imagine that!)

Of course, once I moved my shiny new x64 libraries over to the Windows XP build machine, nothing worked. It didn't work because all of the .exe files that were used to handle the QT magic were 64 bit binaries! So, to get things working, I had to find a way around it.

Since the tools that are used in compiling QT programs basically take some type of data (usually XML) and translate it to code that can be compiled. The code that is generated is nothing more than a text file that is then fed in to a compiler. So, it seemed that I might be able to replace some of the .exe files that QT was attempting to call, and end up with something that worked, even if it was something the QT guys would turn up their noses at.

My first attempt was to copy all of the .exe files from the /bin/ directory of a 32-bit install over the .exe files in the /bin/ directory of the 64-bit install. After doing this, I kicked off a build and waited to see what blew up so I could figure out how to fix it. To my surprise, the project built without complaining.

I figured it couldn't POSSIBLY be THAT easy. But, it looks like it is. The binary that was built seems to work on x64 versions of Windows when cross-compiled from a 32-bit version of Windows.

Friday, February 19, 2010

Hands on with the Unidata WPU7700 (and a Quickphones update)

First, two quick updates on the Quickphones QA-342. First, the battery on that phone finally died. The available information on the expected life of the battery seems to vary a bit depending on which site you are looking at. The site I purchased the phone from claims it should last 7 days. The Quickphones release notes claim 70 hours. Based on this, I figured if I got 70 hours out of it, I would call the battery life a success. After three days, the phone still showed 50% of the battery available. After 9 days, it was finally dead. To put some quick perspective on the battery life. This isn't a situation where I just put the phone on the desk and waited for it to die. I actually used it during that week. I probably spent 30-45 minutes talking on the phone. (Which, while not a lot of time, should have at least a small impact on the battery life of the phone.) So, the verdict on battery life for the Quickphones is an overwhelming two thumbs up.

Second, I heard back from the Quickphones guys. They said they are looking in to the multiple AP issue. If they could get this fixed, I would be sold on these phones for day to day use. As I mentioned in a previous post, the voice quality isn't great, and the shape of the phone could use some work. But, both of those things aren't annoying enough to keep me from using the phone.


However, while I was getting no response from Quickphones, I decided to pick up a Unidata WPU7700. From what I could tell, this phone offered the next best battery life in a Wifi phone.

The phone itself is built well, and feels pretty hefty. Not that I would recommend dropping it, but it does feel like it could take a drop or two. (Assuming the screen didn't break, of course.)

Out of the box, each button press resulted in a beeping noise. This was extremely irritating. Fortunately, hitting the menu button, and switching to the "sounds" menu, I was able to shut that off. This made the phone a lot more pleasant to use.

This phone quickly connected to my WPA2-PSK network. And, after figuring out how the text input worked, I was able to quickly get it signed on as an extension on my Asterisk box. So, I got on quickly to the first test I wanted to run. The battery life test.

I let the phone get a full charge, and then unplugged it from power and left it sitting on the desk. About 21 hours later, the phone was beeping and complaining it was low on power. This was shocking, to say the least. This phone was supposed to have 40 hours of stand-by battery time.

While configuring it, I had turned on the internal web server. So, I wondered if that was keeping the phone more awake that it needed to be. I shut it off, and charged the phone back up completely, and left it sitting on the desk again. About 21 hours later, the phone was again beeping and complaining that it was low on power.

Since I don't see any more options for adjusting the power savings on the phone, I have to conclude that the battery life on this phone is nowhere near what the manufacturer claims. (And down right pitiful compared to the Quickphones!)

As for the sound quality of this phone, it was quite good. It is on par with any cordless analog phone you would find.

One of the more interesting things about this phone is all of the crazy features it has. It has a small web browser built in, some text messaging functionality, a calculator, and a bunch of other little applets. For many, or perhaps most, of the applets, I can't see a point to them. It is functionality that exists on other pieces of equipment that exist in a normal business or even a house. The one feature the Unidata phone has that is great, is the ability to do 802.1X. (Or, WPA(2)-Enterprise, if you want to call it by that name.) I didn't test the functionality, since I have not had time. But, for businesses that care about security, this would be a major win.

Monday, February 15, 2010

QuickPhones follow up....

Well, it has been a bit over a week with the QuickPhones QA-342. So, I thought I would follow up with my view on the hardware so far.

So far, I have not had time to dig in to the multiple AP problem any farther than I had during the last post. But, to get the phone working, I set up a NetGear AP with a different SSID. After doing that, the phone has been reasonably stable.

One of the key things that interested me in this phone was the battery life. So, I let it charge over the weekend that I got it, and have left it powered on, our of the cradle for the last 7 days. Looking at the phone just now, it shows the battery is at 75%. As a result, this phone certainly meets my needs for battery life!

I REALLY wish I could say that the rest of the phone meets my needs. Since I started using it, I have come up with a pretty hefty list of complaints about the phone. Obviously, the first one is with the multi-AP support.

I cut the QuickPhones guys a lot of slack initially about this. Mostly because they were so responsive about getting me access to the 4.0 firmware. It seemed like they were a solid company that really wanted to make a top-notch product. In the last week, my opinion has changed. Early last week, I e-mailed my list of issues to their support people, and included information about my credentials for wireless and offered to help them in debugging the issues. That e-mail seems to have fallen on deaf ears. Later in the week, I pinged them again to verify that they had gotten my first e-mail. Again, I got no response.

Now, let me go off on a bit of a side rant here. I did something in my e-mail to support that I rarely do. I gave them information about my credentials as a wireless networking professional, and wireless network software developer. I generally don't do this because I have been in too many big box electronics stores where the sales people claim to be experts on a topic by nature of working for that store. I don't want to come off sounding like those people. That said, I did include that information in my e-mail to their support people. The main reason being that I liked the build of the phone and wanted to help them get the issues hammered out so I could purchase more of them.

Which leads me back to the additional issues I have seen. The sound quality on the phone is sub-par. There was a lot of static in the background when I was talking to someone. This wasn't comfort noise type static, this was pops and clicks as if packets were being lost in transit. Since it is wireless, and running at 2.4Ghz, I can understand a little bit of that. But, when I compare it to my older D-Link phone the sound quality is night and day. The D-Link was crystal clear, the QuickPhones was less than ideal.

The other sound quality issue I had is with the volume. I will be the first one to admit that I have been to too many loud concerts, and played my stereo louder than I should. So, I don't have the best hearing in the world. So, I usually like to have my phones turned up fairly loud so I can hear everything clearly. When the QuickPhones was to quiet, I tried to use the volume buttons on the side of the phone to increase the in-call volume. From what I could tell, the volume buttons didn't do anything. Again, I have not had time to play with the phone enough to determine if the volume is all the way up, or if the buttons just don't work how I would expect. But, the sound really isn't good.

Which leads me to the next sound related issue. The front of the phone is curved. If the sound on the phone was better, I suspect I wouldn't have noticed this. But, since I was straining to hear the sound, I noticed the curvature of the phone kept it from feeling like it fit nicely against my ear. While this seems like a nit, keep in mind that the vast majority of phones out there are flat. I have to believe there is a reason for that.

So, what comes next? Well, I am supposed to take delivery of a Unidata WPU7700 phone today. It should be interesting to see how it stacks up to the D-Link and the QuickPhones. I also took delivery of an OpenVox A400E board about an hour ago. So, I expect to blog a bit more about the installation an effort involved in getting those running.

Sunday, February 7, 2010

Hands-on with the Quickphone QA-342 Wifi Phone

I have been slowly replacing the old analog phones around my house with some nitfy IP phones. Since I am someone that doesn't like to get too tied down by one vendor, I am using a variety of different phones. (Okay, to be fair, it isn't just a one vendor issue. It is also phones that I can get cheap. ;)

To that end, I have replaced my office phone with a Cisco 7960, the phone in my basement with a Polycom 501, and the phone in my kitchen with a Budgetone (model number escapes me at the moment.) All of these phones are tied to my Asterisk box running in the basement.

At this point, there is only one set of phones in my house that are not IP based. Those would be the old stand-by cordless phones that are probably the second most used phones in the house.

Some time ago, I picked up one of the D-Link "flip-phone" SIP phones. (Model DPH-540, IIRC.) The phone worked surprisingly well for a gen 1 wifi phone. The range was great, the sound was good, but the battery was so horrible that the phone was only usable as a toy. Cordless phones around my house can sit out of their charging station for days (sometimes weeks) without being put back. Needless to say, the D-Link was never charged when I wanted to use it.

So, I started to look around to see what other options were out there. My criteria were somewhat simple. The phone needed to have a significant stand-by time, several days at a minimum. Obviously the phone needed to work with some form of secure SSID, WPA-Enterprise would be great, but I would settle for WPA-PSK.

After some digging, I came up with two phones that looked like they would do what I needed. They were the Quickphones QA-342, and the Unidata WPU7700. I managed to track down the Quickphones for about $130, while the best price I could find on the Unidata was $160. So, I decided to go with the Quickphones.

My goal was that this blog post would be a short entry, made about 7 days after the phone arrived. The D-Link had shown me that there are stable wifi SIP stacks out there, so I assumed that the main thing I would looking at with the Quickphones was the battery life.

Boy, was I wrong! The first problem I ran in to was right out of the box. When I scanned for wireless networks, nothing showed up. This seemed insane to me, since there are a HUGE number of wireless networks in my area. (My house alone probably has 12 SSIDs!!!) After messing with the phone for a while, I did manage to see one or two SSIDs every now and then, but never the SSIDs I wanted to use.

In past years, I have worked with the Interop iLabs people on wireless related demos for the shows in Las Vegas and New York. So, I knew that many wireless implementations have silly restrictions on the size of the scan buffer. So, I decided to pair down the number of SSIDs that the phone could see. First, I turned off all of the wireless in my house. After a reboot, the phone could now see all of my neighbors wireless networks! So, I paired down the list of SSIDs I was broadcasting in my house to just the 4 that I needed to use the most.

I rebooted the phone, thinking I had solved the problem, and again, I saw no scan data. So, I hopped over to the Quickphones site to check that my firmware was up to date. There were two firmwares available on their site. However, version 4 required a license key, so I fired an e-mail off to the Quickphones folks to see if I qualified for the upgrade, and then loaded version 3 on my phone.

Version 3 made no difference. I still couldn't see my SSIDs in the scan. Since I had read of some of the reviews about the phone that it had issues with multiple APs on the same SSID, I shut down all of the radios in my house except the one that was nearest to me. Surprisingly enough, the phone could now see the network, connect to it, and make a call. Thinking that it might be some weirdness in the software that prevented the initial setup when multiple APs were around, I turned the other APs back on, and rebooted the phone. After the reboot, the phone just bounced between scanning, and joining.

The next day, I checked my e-mail and found that the Quickphones people had responded to my initial e-mail within an hour of my sending it. (Downright amazing if you ask me. I can't tell you how many companies have ignored e-mails from me!) In the e-mail was the code I needed to upgrade to version 4. So, I quickly loaded up version 4, did a master reset, and a fresh scan.

My SSIDs show up this time! Life was finally going to be good! So, I attempted to connect to the SSID, but wasn't able to. Every time I put the key in, it complained that it was incorrect. So, I shut down all but one of the APs again, and connected. That method still worked at least!

Once I had done this, I went in to the settings, and turned on the new setting for multiple AP support. I then turned my other APs back on. The phone remained connected to the AP that I was sitting next to! Life again, seemed to be looking up. So, I rebooted the phone to see what would happen. After the reboot, the phone went between scanning and network down mode a few times, then switched to joining, and proceeded to sit there on that screen for a good half hour.

At this point, I am starting to run out of ideas. It is unclear to me if the issue I am running in to is multiple APs, or if it is some incompatibility with the Trapeze wireless gear that I am using. Fortunately, I have a small pile of wireless gear sitting around my house, so I should be able to narrow it down. In the mean time, I am going to also launch a pre-emptive e-mail to the Quickphones folks and see if they are aware of this issue, and if I can help them figure out what it wrong.


More as it develops.....