TrackPoint controls in Linux

So it seems as if the most recent updates to Kubuntu 12.10 have resulted in a stable graphics experience on my NVidia optimus chipset. As a result, I can now live in penguin land 24/7.

However, transitioning from a fully Lenovo-supported OS (Windows) to a “we’ll allow it” OS (Linux) presents it’s own set of issues. One can mostly muddle through with enough Google-fu, but one thing that has always vexed me is configuring my TrackPoint (the “red eraser” that TPs are infamous for). The default pointer calibration settings exposed by KDE’s system settings are pretty minimal – only allowing me to configure a handful of settings that would apply to both the TrackPoint as well as the touchpad. Not very fun. I set about digging, and found something that worked for my ThinkPad T530:

  1. Install Sysfs: sudo apt-get install sysfsutils
  2. Then, enter a root shell: sudo su
  3. Observe the values of the two settings we care about:
    1
    2
    cat sys/devices/platform/i8042/serio1/serio2/speed
    80

    and

    1
    2
    cat /sys/devices/platform/i8042/serio1/serio2/sensitivity
    110
  4. To alter your TrackPoint’s speed, echo -n 110 > /sys/devices/platform/i8042/serio1/serio2/speed. Possible values fall within the the range of 0 – 255, inclusive. Since the results are immediate, you can use trial and error to find a suitable speed.
  5. To alter your TrackPoint’s sensitivity, echo -n 210 > /sys/devices/platform/i8042/serio1/serio2/sensitivity. Possible values fall within the the range of 0 – 255, inclusive. Since the results are immediate, you can use trial and error to find a suitable sensitivity.
  6. Once you’ve settled on the appropriate values, you’ll need to make things permanent. Open your rc.local file and insert the following lines before the “exit 0″ return statement:
    1
    2
    echo -n 160 > /sys/devices/platform/i8042/serio1/serio2/sensitivity
    echo -n 110 >  /sys/devices/platform/i8042/serio1/serio2/speed

    Be sure to replace my values with the ones you deduced in the previous two steps.

And that should do it! There’s a graphical tool for managing this, called configure_trackpoint, but it has a whole bunch of Gnome dependences. *shudders*

The Clouuuud

Yes, the “cloud”.  I hate that buzzword.  I see it everywhere, and seemingly every day I see tech articles and tweets extolling its virtues, heralding it as the next big thing – a means to trade being tethered to a general-purpose computer at a desk for being tethered to a faceless corporation’s ecosystem, accessed through whatever locked-down devices they see it fit to support.

Of course, as with anything that takes control and privacy away from users, there is backlash.  Tonight at 11: are your Facebook photos really private?  The truth may surprise you.  Next thing you know you have parents making their children wear infrared LED hats to birthday parties.  In order to capitalize on this panic some services have started advertising themselves as an alternative to cloud storage.  Not all of them, however, understand what “cloud” storage means.

Like this recent email from my credit union (emphasis mine)…

Introducing My Virtual StrongBox® – our New Online Safe Deposit Box Service!

My Virtual StrongBox allows you to safely store copies of critical documents such as birth certificates, insurance policies, wills and more online, without having to rely on the “cloud.”

With My Virtual StrongBox, you have immediate access to your documents – where and when you need them – with the assurance that they are stored on the same dedicated secure devices where we keep your eStatements and other financial information. Plus, as a Northwest Federal eStatement user, you have access to 100MB of document storage at no charge.

Start safely storing and organizing your important documents. Login to your eStatements directly or through Online Banking, to create your own My Virtual StrongBox today.

TL;DR version:

Vizzini!

My credit union

Inigo Montoya

Me

Implementing Custom Alert DialogFragments

So, I’ve been working on further revisions to my app. The most notable of these revisions involves switching action bar implementations from GreenDroid to ActionBarSherlock.  While this will benefit me tremendously as I’ll be able to use native Android 3.0+ components when running on those platforms, it means that I need to start using Fragments as well as the rest of the Android Support Package.

Refactor Refactor Refactor

I’ve be saving much of what I have learned for another post, but I felt like sharing this tidbit right now.  One thing that quickly became apparent was that a custom dialog that I was using (with positive/negative buttons) was not going to cut it, as the system dialogs were rendering the button strip differently…

The button strip isn't displayed correctly

This is implemented via…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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"
 android:minWidth="280dip"
 android:padding="10dip"
 android:id="@+id/layoutPageDialogRoot"
 >
  <EditText android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:id="@+id/editTextPageNumber"
   android:inputType="number" />
  <TextView android:id="@+id/textViewMaxPages"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content" android:text=""
   android:gravity="right">
      <requestFocus></requestFocus>
  </TextView>
  <LinearLayout android:layout_height="wrap_content"
   android:id="@+id/linearLayout1"
   android:layout_width="fill_parent">
      <Button android:text="Go!"
        android:id="@+id/buttonOk"
        android:layout_height="wrap_content"
        android:layout_weight="1"
    android:layout_marginTop="3dip"
       android:layout_width="0dip" />
      <Button android:text="Nevermind"
        android:id="@+id/buttonCancel"
        android:layout_height="wrap_content"
    android:layout_weight="1"
    android:layout_marginTop="3dip"
       android:layout_width="0dip" />
  </LinearLayout>
</LinearLayout>

While this may seem like a minor UI issue at first, there’s something more to it – Android versions 3.0 and above switched the order of the default dialog button actions, placing the positive button on the right and the negative on the left.  The dialog above is using the pre-2.0 order.  When you’re dealing with muscle memory, such UX concerns become important (especially for deletion dialogs).

So, when refactoring the PaginationDialog into its own DialogFragment, I decided to do more than port my code.  I realized that the native AlertDialog class uses the operating system order, and is styled to match the OS version’s conventions. If only there was a way I could throw my layout into that… wait! There is – AlertDialog.Builder() has a setView() method!

By removing the buttons from the layout XML file and manually inflating it, I can stuff my Views into the Dialog and piggyback off its functionality.

My buttonless layout…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/layoutPageDialogRoot"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:minWidth="280dip"
   android:orientation="vertical"
   android:padding="10dip" >

    <EditText
       android:id="@+id/editTextPageNumber"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:inputType="number" />

    <TextView
       android:id="@+id/textViewMaxPages"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:gravity="right"
       android:text="" >

        <requestFocus>
        </requestFocus>
    </TextView>

</LinearLayout>

And the full DialogFragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class PaginationDialogFragment extends DialogFragment {

    int currentPage, maxPages;

    static PaginationDialogFragment newInstance(int currentPage, int maxPages) {
        PaginationDialogFragment p = new PaginationDialogFragment();
        Bundle args = new Bundle();
        args.putInt("currentPage", currentPage);
        args.putInt("maxPages", maxPages);
        p.setArguments(args);
        return p;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.support.v4.app.DialogFragment#onCreate(android.os.Bundle)
     */

    @Override
    public void onCreate(Bundle savedInstanceState) {
        currentPage = getArguments().getInt("currentPage");
        maxPages = getArguments().getInt("maxPages");
        super.onCreate(savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = LayoutInflater.from(getActivity());
        final View v = inflater.inflate(R.layout.page_dialog, null);
        return new AlertDialog.Builder(getActivity())
                .setTitle("Go to page...")
                .setView(v)
                .setCancelable(true)
                .setPositiveButton("Ok!", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //validation code
                    }
                })
                .setNegativeButton("Aww, hell no!", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    }).create();
    }
}

The important lines in the code snippet displayed above are 28, 29 and 32. On 28, the app gets the inflater being used by the Activity. Remember, you should never instantiate a LayoutInflater directly. After that, it’s pretty trivial to generate the dialog’s content (29) and then insert it into an AlertDialog‘s View hierarchy.

Run it and what do you get?

Beautiful!

Now, run it in Froyo to see if the buttons are automagically reordered…

And there was much rejoicing.

Disclaimer: Yes, I know I’m inlining strings that would be best stored in an external file. That’s a step I put off until I’m ready to make a release build.

Action Bar and You!

So I’ve been developing an Android application on and off for the past few months. In the latest iteration of the app, I wanted to make some options that were hidden in a menu more discoverable. The accepted way of doing this in Android is through adopting the Action Bar design pattern. As described in the official Android Developers Blog entry on the matter:

The Action bar gives your users onscreen access to the most frequently used actions in your application. We recommend you use this pattern if you want to dedicate screen real estate for common actions. Using this pattern replaces the title bar. It works with the Dashboard, as the upper left portion of the Action bar is where we recommend you place a quick link back to the dashboard or other app home screen.

Awesome, right? A unified UI paradigm for Android. Let me just add the widget to my Activity… umm, uhh… where is it? Nowhere in the Android 2.x SDKs. Instead, Google decided to kill two birds with one stone and encouraged developers to look at their source code for the Google I/O schedule app. This way people could not only learn how to use and implement the pattern, but they can also see what a well-written Android app is supposed to look like.

That’s all well and good, but what about the lazy efficient among us, who don’t want to reinvent the wheel and write the same instrumentation every time we make an Activity? Once again, Google to the rescue. With the advent of library projects, the ADT plugin for Eclipse makes importing standalone/reusable components into your existing project easy. After this feature was made available, the number of rich, third-party libraries for Android skyrocketed. Among these were several that had reusable Action Bar controls. After some experimentation, I settled on GreenDroid.

So, GreenDroid

GreenDroid’s claim to fame is the notion that you need to alter very little of your existing code to get attractive, functional UI components. Since my project was mostly complete at the time of implementation, this was a huge plus. And, for the most part, it ended up being true.
Read the rest of this entry »

Fun With Spambots

Spambots exist to teach people a lesson – never give your credit card numbers to websites proffered by nubile girls with webcams and far too much free time on their hands. These bots usually follow a script of “Hi, how are you? Oh, that’s nice. I’m bored and fresh out of the shower. I’m going to go on my webcam and expose myself on the internet (because that’s what us twentysomethings do). Here’s the site!”

Recently, one messaged me on AIM (yes, I still use it), so I decided to have a little fun with it. The conversation is after the break.
Read the rest of this entry »

Logo WTF

A funny ad found in a recent issue of Popular Science…

Shoe ad

Methinks they didn't think their logo choice all the way through (click to zoom)

File upload gotcha…

I’m currently working on some much-needed updates to an AppEngine service that processes and serves content based on a CSV version of a spreadsheet.  One of these new features is the ability to upload the spreadsheet via an HTML form.  Of course, being early-ish on a Sunday morning, there was a little head scratching…

1
2
3
4
5
6
7
8
<html>
  <body>
    <form action="/admin" method="post">
      <div><input type="file" name="csvfile" accept="text/csv"></div>
      <div><input type="submit" value="Update CSV"></div>
    </form>
  </body>
</html>

Looks ok, right? Not quite. self.request.get("csvfile") only returns the file name! I must be forgetting something. After my coffee kicked in, I took another look at it and facepalmed. The following modification returned what I wanted.

1
<form action="/admin" enctype="multipart/form-data" method="post">

Sure enough, adding the “enctype” attribute has my code returning the contents of the CSV file.

Moral of the story? Don’t code before your morning intake of caffeine.

I’m alive!

Someone pinged me to say that I need to update this more often… or at all, really.

My goal with this blog was to fill it with useful content, like clever hacks, product reviews and original musings. I didn’t want to turn it into a diary. And I won’t. That being said, here are a few things I feel are of note…

  • Droiiiiiiiid… Yeah, I dropped the Storm like a bad habit.  I’m now rocking a HTC Droid Incredible, and it lives up to it’s name.  The minute I was able to, I rooted it and installed CyanogenMod 6 to get the stock Android experience (plus some).
  • Vrooooom… A car, I need a new one.  I’m currently trying to decide between a hybrid or ICE Honda Civic.  I’d go with the extra $$$ for a hybrid, but the reduced acceleration is kind of a turn off.  I figure this is a decision best made after test driving.  Don’t want to put the horse before the carriage.
  • 10001101… I’ve been dabbling in writing Android applications.  Of course, finding the motivation to follow through on a junk project is very difficult.  Work still has me pounding away at ASP.NET/C#.  For all of the negative flack MS gets, I really do like the features of the ecosystem.

I’ll be updating the site/blog some more over the next few weeks.  I promise!

Twitter for BlackBerry®

Note: This is an old review that has been sitting in my draft folder.  Rather than delete it, I decided to publish it.  The content may be outdated as I was testing the first official release.

Recently, Twitter made waves in the tech world by buying Tweetie (renaming it Twitter for iPhone) and launching their own BlackBerry (App World) and Android (QR here) clients.  I’ve been a long-time ÜberTwitter user, but decided to see what sort of experience the official Twitter app for BlackBerry could provide.  Click past the jump for my review!

Read the rest of this entry »

Pardon My Dust

Still getting things set up here.  Looking forward to getting back into the blogging saddle.