Tales of the Cocktail 2012 App

Quick note because people have been asking.

The Android app for Tales of the Cocktail 2012 is available in Google Play Store. If you had the previous app installed, you should simply be able to update. Otherwise find it here.

Tales of the Cocktail 2012 for Android

This year the app is also available for iOS. You can find it in iTunes

Tales of the Cocktail 2012 for iOS

It was very interesting building the iOS app. A lot of pre-held myths about developing for iOS were shattered in the process. In fact I've been (gasp) doing quite a bit more iOS dev over the last 6 months than Android dev. If I get time I may make a post or two about it.

Enjoy the apps!

ImageMap for Android

Update September 12 2013

I have disable comments for this post. I am amazed that this old code is still getting so much attention. I feel that the code has become dated and needs a good refactoring. It needs to be tested with newer version of android SDKs. The touch handling needs to be cleaned up. If you find yourself still compelled to use this code, and you have questions/issues, PLEASE direct them to the GITHUB issues list for the project.

GitHub.

Update March 6 2013

Thank you for all of the interest in this widget.

I have become a bit overloaded with work in recent months and I have regrettably been unable to reply to comments in anything near a timely manner.

If you have a question about features or have run into a shortcoming or bug in this widget, I am recommending that you open an issue on GitHub.

This will provide me a better way of tracking problems and questions and may enable you to get help from other people who have used/forked the repo.

As my time frees up (hopefully) I will do my best to create examples and fix bugs. This widget is really due for a hard core refactoring.

  • original post -

I found myself needing to display an image and have the user tap on different parts of the image to navigate and perform actions.  After sketching out what I needed, it became apparent that what I wanted was something like an HTML map tag, but in an Android view.  A bit more thinking and I decided this would be a useful widget to have available.

The resulting code:

  • Supports images as drawable or bitmap in layout

  • Allows for a list of area tags in xml

  • Enables use of cut and paste HTML area tags to a resource xml  (ie, the ability to take an HTML map and image and use it with minimal editing)

  • Supports panning if the image is larger than the device screen

  • Supports pinch-zoom

  • Supports callbacks when an area is tapped.

  • Supports showing annotations as bubble text and provide callback if the bubble is tapped

The code can be found in GitHub - Android Image Map

Update 2 March 2012: Added an option to always resize the image using the original image bits. Certain images, when resized up and down lose a lot of their image quality. Always resizing from the original bits gives the best possible image, but requires extra memory. The tradeoff is now up to you.

Here are some quick notes about the implementation, I'll make some follow up posts with more details about some of the key areas.

The ease of associating the map with the img matches the ease of doing so in an img tag in HTML.  Just supply the name of the map to use in the layout:

<!--?xml version="1.0" encoding="utf-8"?-->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ctc="http://schemas.android.com/apk/res/com.ctc.android.widget"
    android:orientation="vertical"
    android:layout\_width="fill\_parent"
    android:layout\_height="fill\_parent"
>
<com.ctc.android.widget.ImageMap
    android:id="@+id/map"
    android:layout\_width="fill\_parent"
    android:layout\_height="fill\_parent"
    android:src="@drawable/usamap"
    ctc:map="usamap"/>
</LinearLayout>

The area map is specified in your project at res/xml/map.xml

One difference over HTML maps is that each area must have an id. I went back and forth on this requirement, and I may change the code to allow for areas without id. The code will use the name attribute if present, otherwise it will look for title or alt. The current implementation also requires a name for each area, though this could be changed easily.

<!--?xml version="1.0" encoding="utf-8"?-->
<maps xmlns:android="http://schemas.android.com/apk/res/android">
<map name="gridmap">
<area id="@+id/area1001" shape="rect" coords="118,124,219,226" />
<area id="@+id/area1002" shape="rect" coords="474,374,574,476" />
<area id="@+id/area1004" shape="rect" coords="710,878,808,980" />
<area id="@+id/area1005" shape="circle" coords="574,214,74" />
<area id="@+id/area1006" shape="poly" coords="250,780,250,951,405,951" />
<area id="@+id/area1007" shape="poly" coords="592,502,592,730,808,730,808,502,709,502,709,603,690,603,690,502" />
</map>
<map name="usamap">
<area id="@+id/area1" shape="poly" coords="230,35,294,38,299,57,299,79,228,79" />
...
</map>
</maps>

The image itself is placed in res/drawable-nodpi so that the system will not attempt to fit the image to the device based on dpi. This way we are guaranteed that our area coordinates will map properly to the displayed image.

Here is a sample activity that finds the view in the layout and adds an on click handler

public class ImageMapTestActivity extends Activity {
    ImageMap mImageMap;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // find the image map in the view
        mImageMap = (ImageMap)findViewById(R.id.map);
        // add a click handler to react when areas are tapped
        mImageMap.addOnImageMapClickedHandler(new ImageMap.OnImageMapClickedHandler() {
            @Override
            public void onImageMapClicked(int id) {
                // when the area is tapped, show the name in a
                // text bubble
                mImageMap.showBubble(id);
            }
            @Override
            public void onBubbleClicked(int id) {
                // react to info bubble for area being tapped
            }
        });
    }
}

There were lots of challenges with this view. I'll post some follow up thoughts over the next few days along with a bit of discussion about the implementation.

Enjoy the code!

Android SDK/ADT r12 emulator screen problem

I had been eagerly waiting to get done with my current project so I could upgrade the ADT/SDK for android to r12.  I'm glad I waited.

When I fire up the emulator after updating I get the following screen.

Update :  Thankfully, it's a known problem  http://code.google.com/p/android/issues/detail?id=18299

I had used -scale 0.5 to get an emulator window that fits my laptop screen.  The simple workaround is to just use -scale 0.49 until the r13 ADT is out.

Tales of the Cocktail - the Android App

I'm excited to have a new app released in the Android Market today.

This one is an app to support the premier cocktail industry event in New Orleans - Tales of the Cocktail.

Going to tales?  Have an android device?  Check out the app!

Tales of the Cocktail in the Android Market

 

If you run into problems, leave a comment.  If you like it, kindly leave a review.

 

Google IO 2011 - Day After Digest

On my way back to New Orleans from San Francisco.  Great trip out to Google IO 2011. While it is buzzing fresh around my information soaked brain cells, here is my Day-After-Digest.

If I wasn't motivated to be an Android developer three days ago, I'd darn sure be now. Yes there are a zillion devices to worry about compatibility on. Yes there is the issue of coding to fit several versions of the OS. I will swallow that pill to reach the over 400,000 new activations every month. The acceleration of Android adoption shows no sign of waning. Get on board now.

My show highlights:

Android 3.1 addresses tablet woes. This refresh will make Android tablets a bit more viable. Now the adoption of Android tablets will be firmly in the hands of the developer. If you have an app in the market, you absolutely need to adjust it to fit well on the tablet. Some of the most popular apps simply look juvenile today (I'm looking at you TweetDeck).

The new features in Google Market are solid and useful. It is almost as if Google was listening to us. 91 more countries.  The ability to see the list of devices your app will run on. More ways to promote new apps. Better stats and stat filtering. Rewards for being a solid developer. The ability to have larger apps. The ability to support multiple binaries. Thank you.

The Android Tools developers feel our pain. In a way, I think that the giveaway of a tablet to everyone at the show was partly a way to ease the pain of having to develop on the emulator. I was very happy that they showed us their ideas for addressing the emulator speed, even if it was very early code with warts and bruises. Thank you.

I love the soon to be released ADT 11 tools updates. Refactoring layouts. Refactoring styles. Much better visual layout tools. And for those (like me) who prefer to still code the XML directly, much better code assist. I can't wait to get my hands on this. I may even take the time to build it from the upstream source just to get it earlier.

ADK! Heck yes. Once again making it clear that the folks at Google are watching and listening. No more hacking to support USB peripherals. Ok, not exactly true. Much less hacking needed to support USB peripherals. Very nice.

Ice Cream Sandwich. The Googlers were calling it ICS and it was very clear that this has been in the front brain space for a lot of them. This is a very aggressive release and I would not at all be surprised to see the release date slowly creep out. If they want my opinion: Please take the time to do this one right.

Google TV will be a viable app delivery platform.

I still don't quite get the NFC religion.

Next year, they may just have to hold the Android track sessions in the Keynote room.

To the people who got a free tablet,  free chromebook, a free 4G wireless hotspot and still bitched and moaned about not getting a free phone. Grow the heck up.

And speaking of Chrome and ChromeBooks. The new video for the Chromebook/ChromeOS is a home run. In fact I think I'll go watch it again now.

Resize Text to Fit the View in Android

A question I have seen frequently over on  stackoverflow.com in the Android section involves how to stretch, shrink, grow or otherwise resize text to fit the size of a view.

This example shows how to use getTextBounds() from the android Paint class to determine the size text will be when drawn.  With this information you can calculate a textSize and textScaleX to use to get your text the size you want.

For this example I am using an ImageButton and extending it to draw the text over the button image.

The full source for this sample can be found in github: BigTextButton source

The goal is to find values for the textSize and textScaleX that will adjust our text size to fill our View.  Instead of calculating this every time onDraw is called, we will watch for size changes and calculate the values up front.  To do this we override onSizeChanged to get the new view width and height.

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    // save view size
    mViewWidth = w;
    mViewHeight = h;
    // first determine font point size
    adjustTextSize();
    // then determine width scaling
    // this is done in two steps in case the
    // point size change affects the width boundary
    adjustTextScale();
}

From onSizeChanged we call two methods. First one to determine the textSize we use the getTextBounds() method of the Paint object to determine a size for the text at a known textSize. From there we do a simple calculation to determine what textSize would be used to result in the text bounds that we want.

void adjustTextSize() {
    mTextPaint.setTextSize(100);
    mTextPaint.setTextScaleX(1.0f);
    Rect bounds = new Rect();
    // ask the paint for the bounding rect if it were to draw this
    // text
    mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
    // get the height that would have been produced
    int h = bounds.bottom - bounds.top;
    // make the text text up 70% of the height
    float target = (float)mViewHeight\*.7f;
    // figure out what textSize setting would create that height
    // of text
    float size = ((target/h)\*100f);
    // and set it into the paint
    mTextPaint.setTextSize(size);
}

The second method determines the textScaleX value in the same way. We find what the size would be with a textScaleX of 1.0, then do some simple math to determine what scale would result in the width of bounds that we want. This is done in a separate call call from the text size calculation just in case setting the textSize affects the result for scale.  Changing textScaleX will definitely not change the height of the text drawn.

void adjustTextScale() {
    // do calculation with scale of 1.0 (no scale)
    mTextPaint.setTextScaleX(1.0f);
    Rect bounds = new Rect();
    // ask the paint for the bounding rect if it were to draw this
    // text.
    mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
    // determine the width
    int w = bounds.right - bounds.left;
    // calculate the baseline to use so that the
    // entire text is visible including the descenders
    int text\_h = bounds.bottom-bounds.top;
    mTextBaseline=bounds.bottom+((mViewHeight-text\_h)/2);
    // determine how much to scale the width to fit the view
    float xscale = ((float) (mViewWidth-getPaddingLeft()-getPaddingRight())) / w;
    // set the scale for the text paint
    mTextPaint.setTextScaleX(xscale);
}

The final detail is to determine where vertically in the view to paint the text. The boundary rect returned by getTextBounds can help with this. The bounds.top value will actually be a negative value, and it represents the size of the text not including the size of any descenders (parts of letters that extend below the base line). The bounds.bottom value is the size of the descenders. Using this information, we can decide how to position the text in the view. The y value in the call to drawText represents the baseline. Descenders will be drawn below this line. For this example, we adjust the y value so that we are showing the full descenders. This value is saved off as mTextBaseline and is used in onDraw.

@Override
protected void onDraw(Canvas canvas) {
    // let the ImageButton paint background as normal
    super.onDraw(canvas);
    // draw the text
    // position is centered on width
    // and the baseline is calculated to be positioned from the
    // view bottom
    canvas.drawText(mText, mViewWidth/2, mViewHeight-mTextBaseline, mTextPaint);
}

This creates a nice button that automatically adjusts its text size to fill the view.

Once again, the complete source is in github here:  BigTextButton source

Enjoy.

 

 

My French Quarter Festival 2011 app

Living in New Orleans, we love our festivals.

Coming up this weekend is the French Quarter Festival. Four days and nights of completely free entertainment on a dozen stages all around the historic Vieux Carre.

So I went looking for an App for my Android phone to view the schedule. To my dismay, there was none. iPhone users, they got you covered.

Well, that just won't do, and so began a furious week of code cranking which resulted in my very own French Quarter Festival 2011 Schedule App. I even decided to publish it to the Android Market just in case someone else might find it useful.

So check it out in the market - French Quarter Festival 2011.

If you use it and like it, please rate it in the market. If you have trouble let me know in the comments.

View Animation with Full Layout Support

Herein lies a tale of frustration with View animations and through necessity, the invention of a ViewGroup that supports smooth animation of views through the animation of the underlying view layout.

For those interested in getting directly at the code, you can find it over at github: DynamicLayoutViewGroup.

Recently I found myself working on an application that required some View animations.  I set about coding up a set of views that would rearrange themselves based on which one was selected.  After some serious code wrangling and some quality time spend reading through the android.view.animation.* source code, I came to the realization that the 2D animation support had two shortfalls that were making my project difficult.

First, the animations don't actually change the position of the views.  It is left as an exercise for the programmer to deal with actually getting the view positions updated at the end of the animation run.  This was even confirmed by Google framework coding guru Romain Guy, who said:

A translate animation does not actually change the position of the view. It changes where it is drawn, not its position within the layout. As such, the values returned by left/top/right/bottom are still valid. Updating the view's coordinate (and more generally its whole geometry) is already planned for a future release as part as the support for static transformations. This is however considered as a low priority for now.

This was not in and of itself a show stopper.   However, the second problem was.  The only animation available to change the size of a view is the ScaleAnimation.  However, it does just that, changes the scale.  The view does not get to redraw itself to fit the new size as it moves, rather a snapshot of the view is scaled up.  Particularly vexing is that this causes 9 patch images to scale up as though they were simple bitmaps.  Not acceptable for my needs.

It turns out that both of these issues could be addressed if instead of animating the View itself, we were to animate the View's layout.   If for each step of a translation and resize, the View were to have its onLayout and onDraw called, we could achieve truly smooth View animations.  The images would not stretch in awkward ways and the content of the view could reshape to fit the new layout.

Fast forward through some furious coding and we arrive at the DynamicLayoutViewGroup.

The specific type of layout that this ViewGroup is designed to manage is one where the selection of a child view causes the position of the views to change.  Most common use is a menu of items that keeps the selected item centered on the screen.  In this kind of layout, when a new child is selected, the views will move so that the new selected child is centered.  In my specific use case, we also wanted the selected child to be larger than the other items.

On to the solution. The concept is simple.  There is a user supplied LayoutModel which is used to indicate a Rect for the bounds of each child view.

public interface LayoutModel {
    /\*\*
    \* called when the parent view size changes
    \*/
    public void onSizeChanged(int width, int height, int oldw, int oldh);
    /\*\*
    \* called for each child (by index) to get a layout Rect
    \*/
    public Rect getLayoutRect(int pos, int selected);
}

When the child views are laid out, the Rect returned is used in the call to the child.layout() function and the values are saved off as the start position for the view should it want to animate.

protected void layoutChildren() {
    // animation code omitted see below
    // when no animation is happening, we simply
    // Layout children based on the values provided by
    // the LayoutModel
    View v;
    ViewHelper vh;
    for (i = 0; i < getChildCount(); i++) {
        v = getChildAt(i);
        r = mLayoutModel.getLayoutRect(i,\_selected);
        v.layout(r.left, r.top, r.right,r.bottom);
        // also, store this position as the start position
        // for the next time we want to animate
        vh = getViewHelper(v, i);
        vh.setStartPosition(r.left, r.top, r.right,r.bottom);
    }
}

When a new child is selected, the LayoutModel is called again to get the new position. It is up to the implementation of the LayoutModel to set the positions based on the new selected child. These new values are stored off as the target Rect for the views. At this point, a flag is set that indicates animation should occur and layout is called.

protected void animateLayout() {
    int i;
    // set up ViewHelpers to animate the views to a new location
    // by asking for new layout Rect and passing in the updated
    // selected item position. The current position is already
    // stored away
    View v;
    ViewHelper vh;
    Rect r;
    for (i = 0; i < getChildCount(); i++) {
        v = getChildAt(i);
        vh = getViewHelper(v,i);
        r = mLayoutModel.getLayoutRect(i, \_selected);
        vh.setTargetPosition(r.left,r.top,r.right,r.bottom);
    }
    // once all the target positions are gathered
    // set the "please animate me" flag and call layout
    mAnimating = true;
    mStartDrawTime = -1;
    layoutChildren();
}

With the animating flag set, we revisted the layoutChildren() function to see how the view animation works. It is similar to the android.view.animation.* classes, using a percentage from start to end time to determine where along the animation path to set the view. Once the views are laid out at the intermediate location, the function uses post() to place another call to layoutChildren in the queue. This happens until the animation duration is elapsed.

protected void layoutChildren() {
    int i;
    Rect r;
    // Is the view animating to new layout positions?
    if (mAnimating) {
        if (mStartDrawTime == -1) {
            mStartDrawTime = SystemClock.uptimeMillis();
        }
        // here we are moving the views and forcing layout
        // to update the child view locations
        // get current time
        long timenow = SystemClock.uptimeMillis();
        // if animation time not expired
        if ((timenow - mStartDrawTime) < mAnimationDuration) {
            // determine time as %
            float tdiff = (float) timenow - (float) mStartDrawTime;
            float timeslice = mStartDrawTime == -1 ? .05f : tdiff
            / (float) mAnimationDuration;
            // use layout helper to layout views
            // the layoutAtTime method will determine where to
            // position the view based on % between the start position
            // and requested end position. timeslice is a % value from
            // 0 to 1 depending on how much animation time remains.
            // the view helper is also where the interpolator is
            // called.
            for (ViewHelper vh : helperList.values()) {
                vh.layoutAtTime(timeslice);
            }
            mLastDrawTime = SystemClock.uptimeMillis();
            // post event to do the layout again
            post(new Runnable() {
                public void run() {
                    layoutChildren();
                }
            });
            } else {
            // the animation duration is passed
            // set mAnimation false
            mAnimating = false;
            mLastDrawTime = -1;
            mStartDrawTime = -1;
            // and call layoutChildren to set final locations using
            // non animation branch of layout
            layoutChildren();
        }
        } else {
        // the non animation layout code is here
    }
}

The code is available at github: DynamicLayoutViewGroup.

There are two examples included of LayoutModels. The default model lays the views out as a horizontal list of views with the selected view centered. The other example sets a different LayoutModel which shows another take on laying out the views on a single screen.

This will all likely become less useful as the new Android 3.0 render script animations become the norm, however a good adventure in coding is never wasted.

Have fun digging in the code and by all means, post questions.

Stop that! Eclipse external folder scanning

I have a love hate relationship with Eclipse and the ADT.

Mostly it's a hate relationship.  I'm not going to go into that now (and yes I know I really need to try ).

To the point.  I found myself really wanting to debug through the Android source.  To do this, when I was sitting in one of Eclipse's nice psuedo-code-from-class views, I hit the find source code button and added the root of Android source.  This actually worked.

The downfall is that ever since then Eclipse has been non-stop scanning and rescanning this "external folder".  The performance has gone to heck and my patience went along with it.   So if you happen to have fallen into this predicament and would like to undo the setting and haven't yet figured out exactly where to look, here is my prescription:

Select the Project | Preferences menu.

Select Java Build Path

Expland Android (I was using Android 2.1 at the time)

expand android.jar

See that Source attachment is set to your source directory.  Select it and hit the Remove button.

wait patiently.  breathe.

 

 

Starting at the right place

Here we go, yet another Android coding blog.  Let's get this one item out of the way up front.

If you are going to write apps for Android (and you are right? otherwise why the heck are you reading this?) you are giving yourself a huge handicap by not having the Android source code on your Dev machine(s).

So if you get nothing else out of this blog, I implore you, please, take the time to pull the source code and learn the layout.  Learn the coding styles.  Read the comments.  I honestly believe that you should not be hired as an Android programmer unless you have pulled the code at least once.

http://android.git.kernel.org/

http://source.android.com/source/download.html