Monday, August 3, 2009

We twit it

For better or worse we created twitter account. You're more than welcome to follow us.


Friday, April 10, 2009

Binding to a service fails for child activities of an ActivityGroup

This is more of a beginner's help to other beginners. I couldn't find much about this on the internet, so I decided to speak up.

If your service connection doesn't open as expected and your LogCat complains in the following way:

Binding with unknown activity: android.os.BinderProxy@43535e18

you're most likely using an extension of ActivityGroup, which is most likely TabActivity.

For binding to MyService you probably use something like this:


Intent intent = new Intent(this, MyService.class);
if (!bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
throw new IllegalStateException("Binding to service failed " + intent);
}


It appears that if the context is a child activity from an ActivityGroup your binding is doomed.
I couldn't find any explanation to that fact at the moment. Maybe it's a bug or maybe it's a feature. The more important is to have a workaround.

The workaround is simple. Redesign it to have binding functionality in the ActivityGroup.

If you can't do so, just use Activity.getParent() method to pass ActivityGroup as the context. Of course in this case the call will fail if you try to use the child activity separately from ActivityGroup.

for additional comments visit personal blog

Saturday, February 28, 2009

How to display a local file in the Android WebView

For some reason WebView is not displaying local files found on sdcard or in other directories of an Android phone. It was doing so prior to the official 1.0 SDK release, but not anymore.
There was another way to display local files - by overriding WebViewClient.shouldOverrideUrlLoading, but since 1.0 SDK it's gone.

Right now community agrees that a relatively easy way to make WebView display local files is through a custom ContentProvider.

It's not quite obvious what needs to be overridden for this specific task. I couldn't find a complete example online. So I decided to share mine.

There are 2 steps
  1. Create new class that extends ContentProvider and override essentially 1 method openFile. Method onCreate can be empty returning true and the rest can just throw UnsupportedOperationException.
  2. Add a provider entry to AndroidManifest.xml.
Provider implementation

Note, that method constructUri is just a utility method that can be used to construct provider specific URI out of a file path.

package com.tourizo.android.content;

import java.io.*;

import android.content.*;
import android.database.*;
import android.net.*;
import android.os.*;

public class LocalFileContentProvider extends ContentProvider {
private static final String URI_PREFIX = "content://com.tourizo.android.localfile";

public static String constructUri(String url) {
Uri uri = Uri.parse(url);
return uri.isAbsolute() ? url : URI_PREFIX + url;
}

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = new File(uri.getPath());
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
return parcel;
}

@Override
public boolean onCreate() {
return true;
}

@Override
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
throw new UnsupportedOperationException("Not supported by this provider");
}

@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}

}



AndroidManifest.xml entry under application tag

        <provider android:name=".content.LocalFileContentProvider"
android:authorities="com.tourizo.android.localfile"
/>


I hope this was helpful.

Happy hacking! :)

Monday, February 16, 2009

We've moved

Thank you, Mr. Blogspot, for allowing us to map our subdomain to this blog!
Our new official address is http://blog.tourizo.com. That's where it'll stay for a while.
Everything should work as before.

Let's celebrate!

Sunday, February 15, 2009

What is good code?

Well... I have nothing to say about The Da Vinci Code. Looks like that code is much better than mine.

If you're in software development, you've heard phrases "good code" and "bad code" at least once. It's rather safe to say that any code is bad. But does anyone know what a good code is?

In the past I had to answer this question on one of the job interviews. I spoke about technologies, patterns, readability, reliability, extensibility, girls, clubs... well... the last 2 things were part of a different conversation. I got an offer, so I believe my answer was OK.

I sincerely described what I thought a good code should be, but that was developer standards. Who cares about developers? Sorry, guys, that's the reality...

For a software developer his code is a product of his work. There's no work without a client. There are all kinds of clients out there: customers, managers, developers themselves and any combination of the above.

Client makes the rules and eventually owns what the developer had produced. So, if the client is happy with the code, then the code is good. It's that simple.

For a client to be happy with the code it has to meet all his requirements. And a lot of developers would be surprised finding out that, for instance, readability or extensibility are not always part of those.

The reason is developer's standards are not always in line with the client's goals.
Here are a few examples:
  • Chances are the more requirements a client comes up with, the more expensive the product would be. The client is trying to optimize the requirements to fit into the available budget, resources and time constraints.
  • Time is money, but nobody says it works the other way. Clients may have funds, but not enough time to build the whole house, they just need 3 walls, so they can register it as a building at the town hall by the end of the year (don't try it at home).
  • Some managers specialize on making a career growing huge bloated code that will never work. It's understandable. They are salaried employees. Till promotions affect their lives more than stock fluctuations it's naive to assume they care about efficiency more than about number of their reports.
Good code is like good food.
What is good food?
Peanut butter and jelly? Is it still good for you if you have a peanut allergy?
French cuisine? What if you're not willing to spend your daily wage for a huge fancy plate with a tiny piece of liver of a duck that died from cirrhosis?

To each his own. That's exactly the case with software development. If the client is happy with the code, this means the food was good.

P. S.
Client may be happy with the food, but not with the service. That's a different story.

P. P. S.
Your next question is probably "How to make a client happy?" Not only is it a different story, but it's unlikely anyone knows the right answer. I can only define 2 main aspects:
  1. It's important to have a good chef in the kitchen. The one capable of delivering a code that satisfies a client. So if client wants it to be reliable, readable, extensible and medium-rare, the developer has to be able to make it that way (not just well-done).
  2. It's even more important to know what the client really needs. And this part is not easy at all. Unlike with food... Since it's not food related, it can wait till the next time slot for blogging...

Sunday, February 8, 2009

Android sources made easy

I assume if you're reading this you're not going to build your own Android2 and need sources just for reference.

What's the first thing you do to find sources for Google Android? Right, you use that same Google...
Just to find yourself here http://android.git.kernel.org/, where even Google can't help you find anything.

You can invest a few hours of your time setting up the environment downloading everything and searching through it.
You can invest a few hours clicking through the web based interface.

Or you can use this hint. Start from project "platform/frameworks/base.git" found here:
http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree

This project and especially subdirectory "core" should contain most of the API related code, which is probably what you've been looking for.

The page contains link "snapshot" which allows you to download the HEAD revision of base.git repository.

Good luck hacking it. :)

Android TabActivity customization

Half of this weekend was spent in a desperate attempt to customize Android TabActivity.
According to developers community previous Android API releases allowed for tab look and feel modifications.

Unfortunately, mighty Google decided to revoke this ability and, trust me, they're pretty good at doing so.

It's obvious they've done it on purpose and I can't justify their decision. I can only fight back with whatever I have left.

I found a few guys trying to do similar stuff, so I decided to share:

  1. Create a selector drawable in your project. You can just copy paste the one from Android sources called tab_indicator.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <!-- I drew these states myself, that's why they look so ugly. -->
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Non focused states -->
    <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_unselected" />
    <item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/tab_selected" />

    <!-- Focused states -->
    <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/tab_focus" />
    <item android:state_focused="true" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/tab_focus" />

    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@drawable/tab_press" />
    </selector>

  2. You've done item 1 and already hate me because compiler shows errors? This means you've done it right. Now create corresponding drawables for each selected/focused/pressed state. 9-patches are preferable. Errors should go away.
  3. In your TabActivity in onCreate() method after you've constructed all the tabs do the following:

    for (int i = 0; i < tw.getChildCount(); i++) {
    View v = tw.getChildAt(i);
    v.setBackgroundDrawable(getResources().getDrawable(R.drawable.tour_tab_indicator));
    }
  4. Depending on your new tab design you may be already happy or you may notice a thin line of a standard gray color under unselected tabs. It turns into orange upon touch. To fix it your have to use even hackier way. TabWidget contains 2 drawables that actually form those thin lines.

    private Drawable mBottomLeftStrip;
    private Drawable mBottomRightStrip;

    I haven't tried this yet especially on the actual device, but unless they've changed core JVM stuff that should be easy to change a private variable through reflection. Maybe I'll do it later and will post an update, but for now wish you a good luck with that part. :)
Make sure that R used is from your project.

What you've just done - you've replaced background selector drawable set by TabActivity with your own, which may essentially be the same, but uses your own 9-patches.

Aftermath:
This solution is rather a hack way around and relies on the current TabWidget implementation and as a result may not be compatible with future changes to TabWidget.
On the other hand I hope future changes to the TabActivity API will provide a better way for developers to alter tab look and feel.