Fork me on GitHub

Friday, March 15, 2013

Scaloid 1.1 is released

Today I released Scaloid 1.1. You can download it from the central maven repository.

Some key improvements in this version are:

* Dynamically accessing SharedPreferences
* LocalService
* Lifecycle management
* Drops Scala 2.9 support (mainly due to the use of type dynamic in SharedPreferences)

Sunday, March 10, 2013

Introducing LocalService

Activities has a very short-term lifecycle. Even when you rotate your device, the activity is destroyed and newly created. Therefore, a service is needed for a longer term behavior that should persist when the current application is not in foreground. Service is designed to be a pretty powerful tool, that is intended to support inter-process communication. However, I (and probably you) just use a service as an in-process singleton object for most of the time. Just accessing an object in the same VM need not be complex at all. However, the Android Developer Guide misguides developers to write such a horrible length of code:

public class MyService extends Service {
    private final IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        MyService getService() {
            return MyService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private final Random mGenerator = new Random();

    public int getRandomNumber() {
        return mGenerator.nextInt(100);
    }
}
public class BindingActivity extends Activity {
    MyService mService;
    boolean mBound = false;

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    public void onButtonClick(View v) {
      if (mBound) {
        int num = mService.getRandomNumber();
        Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
      }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}
This implementation is full of boilerplate code. In Scala, we can rewrite it as follows:
class MyService extends LocalService {
    private val generator = new Random()

    def getRandomNumber() = generator.nextInt(100)
}
class MyActivity extends SActivity {
  val random = new LocalServiceConnection[MyService]

  def onButtonClick(v:View) {
    random( s => toast("number: " + s.getRandomNumber()))
  }
}
This code snippet does the same thing with the original Java code. All of the clutters are encapsulated in LocalService and LocalServiceConnection, so you can just focus on your idea.

We often initializes a variable with some default value when the service is not yet connected:
val num = if(random.connected) random.service.getRandomNumber() else 0
doSomethingWith(num)

With LocalServiceConnection.apply, the expression above can be shortened as shown below:

val num = random(_.getRandomNumber(), 0)
doSomethingWith(num)

The full source code of these traits can be found here

Friday, March 1, 2013

Prompt user to rate your Android app

To make your app is frequently searched, user rating is important. Prompt engaged user with a dialog that asking a (favorable) vote is a common technique. Although it is some kind of exploit, I think that it is not so dirty, because nobody hurts by doing this. I have googled about it and found a code snippet here. However, that code is too long whereas it does a simple thing. I rewrite it using Scaloid

onCreate {
  val prefs = Preferences()
  val ec = prefs.executionCount(0)
  prefs.executionCount = ec + 1
  if (ec % 20 == 19 && !prefs.wontRate(false)) {
    new AlertDialogBuilder(null, "Please take a moment to rate it.") {
      positiveButton("Rate it", {
        openUri("market://details?id=com.soundcorset.client.android")
        prefs.wontRate = true
      })
      neutralButton("Remind me later")
      negativeButton("No thanks", {
        prefs.wontRate = true
      })
    }.show()
  }
}

Comparing with the original code, this is really simple, isn't it?

Dynamically accessing SharedPreferences

Scala 2.10 ships with a new feature called Type Dynamic, which is borrowed from typeless script language (e.g. Groovy). With this feature, the name of methods and properties can be handled in runtime. One of the practical use case of it is accessing SharedPreference object more concisely. An ordinary way of accessing a SharedPreference property looks like this:

val ec = pref.getInt("executionCount", 0)
val editor = pref.edit()
editor.putInt("executionCount", ec + 1)
editor.commit()

Doing some work on the backstage with Type Dynamic, the code can be improved as follows:
val ec = pref.executionCount(0)
pref.executionCount = ec + 1

Accessing properties becomes more natural. To do this, I wrote some helper classes using Type Dynamic as shown below:
class Preferences(val preferences: SharedPreferences) extends Dynamic {
  def updateDynamic(name: String)(value: Any) {
    value match {
      case v: String => preferences.edit().putString(name, v).commit()
      case v: Int => preferences.edit().putInt(name, v).commit()
      case v: Long => preferences.edit().putLong(name, v).commit()
      case v: Boolean => preferences.edit().putBoolean(name, v).commit()
      case v: Float => preferences.edit().putFloat(name, v).commit()
    }
  }

  def applyDynamic[T](name: String)(defaultVal: T): T = defaultVal match {
    case v: String => preferences.getString(name, v).asInstanceOf[T]
    case v: Int => preferences.getInt(name, v).asInstanceOf[T]
    case v: Long => preferences.getLong(name, v).asInstanceOf[T]
    case v: Boolean => preferences.getBoolean(name, v).asInstanceOf[T]
    case v: Float => preferences.getFloat(name, v).asInstanceOf[T]
  }
}
When you access pref.executionCount(0), Scala compiler expands it to pref.applyDynamic("executionCount")(0), and pref.executionCount = ec become pref.updateDynamic("executionCount")(ec).

The full source code can be found here. You can simply download and enjoy it.

Prompt user to rate your Android app

Now, you can easily access persistent properties. One of the most popular example is prompting engaged user to rate your Android app. I posted about it here.