Tuesday, January 10, 2012

Convert RGB to HSL and vice-versa in Java

I wanted to add colored labels to my Android application and I wanted to make it such that the users need only to select one color, the background color, and the foreground color would be the same color, only lighter or darker.

I thought I needed a RGB to HSL (Hue, Saturation, Lightness) converter, but Android SDK only provides RGB to HSV value, which has some similarity but different.

Based on the above Wikipedia article, I wrote the Java code to convert between RGB and HSL. Hopefully this can be useful to your project.
  • rgb is an int (with alpha ignored) 0xrrggbb
  • hsl is an array of 3 floats:
    • [0] is hue, 0..360
    • [1] is saturation, 0..1
    • [2] is lightness, 0..1
public static void rgbToHsl(int rgb, float[] hsl) {
 float r = ((0x00ff0000 & rgb) >> 16) / 255.f;
 float g = ((0x0000ff00 & rgb) >> 8) / 255.f;
 float b = ((0x000000ff & rgb)) / 255.f;
 float max = Math.max(Math.max(r, g), b);
 float min = Math.min(Math.min(r, g), b);
 float c = max - min;
 
 float h_ = 0.f;
 if (c == 0) {
  h_ = 0;
 } else if (max == r) {
  h_ = (float)(g-b) / c;
  if (h_ < 0) h_ += 6.f;
 } else if (max == g) {
  h_ = (float)(b-r) / c + 2.f;
 } else if (max == b) {
  h_ = (float)(r-g) / c + 4.f;
 }
 float h = 60.f * h_;
 
 float l = (max + min) * 0.5f;
 
 float s;
 if (c == 0) {
  s = 0.f;
 } else {
  s = c / (1 - Math.abs(2.f * l - 1.f));
 }
 
 hsl[0] = h;
 hsl[1] = s;
 hsl[2] = l;
}

public static int hslToRgb(float[] hsl) {
 float h = hsl[0];
 float s = hsl[1];
 float l = hsl[2];
 
 float c = (1 - Math.abs(2.f * l - 1.f)) * s;
 float h_ = h / 60.f;
 float h_mod2 = h_;
 if (h_mod2 >= 4.f) h_mod2 -= 4.f;
 else if (h_mod2 >= 2.f) h_mod2 -= 2.f;
 
 float x = c * (1 - Math.abs(h_mod2 - 1));
 float r_, g_, b_;
 if (h_ < 1)      { r_ = c; g_ = x; b_ = 0; }
 else if (h_ < 2) { r_ = x; g_ = c; b_ = 0; }
 else if (h_ < 3) { r_ = 0; g_ = c; b_ = x; }
 else if (h_ < 4) { r_ = 0; g_ = x; b_ = c; }
 else if (h_ < 5) { r_ = x; g_ = 0; b_ = c; }
 else             { r_ = c; g_ = 0; b_ = x; }
 
 float m = l - (0.5f * c); 
 int r = (int)((r_ + m) * (255.f) + 0.5f);
 int g = (int)((g_ + m) * (255.f) + 0.5f);
 int b = (int)((b_ + m) * (255.f) + 0.5f);
 return r << 16 | g << 8 | b;
}

Friday, January 6, 2012

Add "screenSize" flag to android:configChanges when targetting API level 13 (Android 3.2) or newer

If you're setting "keyboardHidden|orientation" to android:configChanges in order to prevent activity from restarting itself when the device is rotated, you might be surprised when you find your activity restarts itself when running on Android 3.2 or newer.

The reason is, starting from Android 3.2 (API Level 13), rotated device will also trigger "screenSize" change. From attrs_manifest.xml:



        <!-- The current available screen size has changed.  If applications don't
             target at least {@link android.os.Build.VERSION_CODES#HONEYCOMB_MR2}
             then the activity will always handle this itself (the change
             will not result in a restart).  This represents a change in the
             currently available size, so will change when the user switches
             between landscape and portrait. -->
        <flag name="screenSize" value="0x0400" />


It means, if your android:targetSdkVersion is 13 or more, your application will receive "screenSize" config change. Therefore, to prevent your activity from restarting, add "screenSize". E.g.

    android:configChanges="keyboardHidden|orientation|screenSize"

Hope this helps you.

Tuesday, November 29, 2011

Convert UNIX timestamp to human date using command-line

I'm working on a project where date/time are sent in UNIX timestamp. Sometimes when I'm debugging, I want to check whether the usually 10-digit number is correct.

The easiest way I found is using the command-line terminal:


~$ date -r 1322532029 
Tue Nov 29 10:00:29 SGT 2011

Friday, November 4, 2011

List of Inlined/intrinsic methods in Android's Dalvik VM

Android's Designing for Performance document, states:
In addition to all the usual reasons to prefer library code over rolling your own, bear in mind that the system is at liberty to replace calls to library methods with hand-coded assembler, which may be better than the best code the JIT can produce for the equivalent Java. The typical example here is String.indexOf and friends, which Dalvik replaces with an inlined intrinsic. Similarly, the System.arraycopy method is about 9x faster than a hand-coded loop on a Nexus One with the JIT.
Unfortunately, as are other Android SDK documentation, this doesn't tell you much which methods gets inlined with machine code.

Examining the Dalvik VM source code, I found dalvik/vm/InlineNative.c that starts with an introduction:


/*
 * Inlined native functions.  These definitions replace interpreted or
 * native implementations at runtime; "intrinsic" might be a better word.
 */


So the inlined methods are:
  • class String:
    • charAt(int)
    • compareTo(String)
    • equals(Object)
    • fastIndexOf(int, int)
    • isEmpty()
    • length()
  • class Math:
    • abs(int)
    • abs(long)
    • abs(float)
    • abs(double)
    • min(int, int)
    • max(int, int)
    • sqrt(double)
    • cos(double)
    • sin(double)
  • class Float:
    • floatToIntBits(float)
    • floatToRawIntBits(float)
    • intBitsToFloat(int)
  • class Double:
    • doubleToLongBits(double)
    • doubleToRawLongBits(double)
    • longBitsToDouble(long)
That's it! Now don't be so scared to use those methods in your code.

Bonus: fastIndexOf ? What is that? Java's String has indexOf, but never an fastIndexOf.

Answer: The implementation of String's indexOf(int) or indexOf(int, int) that Android uses, calls fastIndexOf if the char to be searched is <= 0xffff (which most probably is). So, if your code has a call of indexOf('x'), the native implementation will still be used.

Saturday, October 22, 2011

AnimationDrawable doesn't start after calling start()

Frame animations (sometimes declared in xml as <animation>) produces an AnimationDrawable. However, in some situations, such as using it as one of the ListView items, the animation doesn't start. Even if you call start().

The solution is simple. Just call stop() and then start().

In the source code of AnimationDrawable, the method start() can sometimes do nothing.

public void start() {
        if (!isRunning()) {
            run();
        }
    }

I have encountered this twice. I hope this helps you.

Thursday, August 11, 2011

Don't use Adapter#isEnabled(int) to disable individual list items (Android)

When we have items in a ListView, we may want to disable some of the items.

Disabling some of the items can be done by overriding isEnabled(int position) on the adapter, but unfortunately the list item dividers are not drawn anymore! Overriding isEnabled(int position) was meant to be used to create some kind of "separator" or "list header".

The correct way to disable an item in a ListView is to call setEnabled(false) on the view of the item itself. In other words, call setEnabled(false) on the view that is returned from getView(int position, View convertView, ViewGroup parent).

Reference: Romain Guy's post on android-developers.

Monday, May 9, 2011

Using convertView and returning the correct view in Adapter's getView in one line!

Are you tired seeing or typing these lines over and over again?

public View getView(int position, View convertView, ViewGroup parent) {
    View v;
    if (convertView == null) {
        v = LayoutInflater.from(getApplicationContext()).inflate(R.layout.row, null);
    } else {
        v = convertView;
    }
    // later:
    return v;
}

Do that in one line!

View v = convertView != null? convertView: LayoutInflater.from(getApplicationContext()).inflate(R.layout.row, null);

This seems easy but at first I didn't think of that!