Fork me on GitHub

Saturday, April 19, 2014

Press-and-hold action on Android

Sometimes we need to receive repeated callback when a user pressing a button continuously. For example, when we make a number picker, press-and-hold on "+" button will constantly increase the number.

Android does not support this explicitly, but we can handle this behavior with postDelayed event. Here are a code snippet doing this:


This works fine, but a little remaining problem is that this code should be encapsulated to be reused in other places. I wrote the following trait doing this encapsulation:


trait PressAndHoldable {
  def basis: android.view.View

  class PressAndHoldListener(interval: Int, onPressed: () => Unit) extends View.OnTouchListener with View.OnLongClickListener {
    var autoIncrementing: Boolean = false
    private val repeatUpdateHandler = new android.os.Handler()

    override def onTouch(v: View, event: MotionEvent): Boolean = {
      if (event.getAction == MotionEvent.ACTION_UP && autoIncrementing) {
        autoIncrementing = false
      }
      false
    }

    override def onLongClick(p1: View): Boolean = {
      autoIncrementing = true
      repeatUpdateHandler.post(new RptUpdater)
      false
    }

    class RptUpdater extends Runnable {
      override def run() {
        if (autoIncrementing) {
          onPressed()
          repeatUpdateHandler.postDelayed(this, interval)
        }
      }
    }
  }

  def onPressAndHold(interval: Int, onPressed: => Unit) {
    val listener = new PressAndHoldListener(interval, () => onPressed)
    basis.setOnTouchListener(listener)
    basis.setOnLongClickListener(listener)
  }
}

This trait is added on Scaloid 3.3 release. So you can just use onPressAndHold method for any android.view.View objects:

val num = STextView("0")
SButton("Increase").onPressAndHold(100, num.text = (num.text.toInt+1).toString)

Saturday, February 1, 2014

Scaloid 3.2 is released

Today I released Scaloid 3.2. This version achieves some feature enhancements including:

Properly handle TableLayout

https://github.com/pocorall/scaloid/issues/68

Enriched android.database.Cursor

Now we can access database in more functional style.

px2sp and px2dip

We can simply convert pixel unit into sp unit or dip unit.
32.px2dip  // convert 32 pixel into dip unit

Scaloid is released to central maven repository.

For a maven project:
<dependency>
    <groupId>org.scaloid</groupId>
    <artifactId>scaloid_2.10</artifactId>
    <version>3.2-8</version>
</dependency>
or for an sbt project:
libraryDependencies += "org.scaloid" %% "scaloid" % "3.2-8"

Simple enhancements on accessing Android built-in Sqlite

Functional programming rocks. However, legacy Java libraries prevent us to stay in such an ideal world. The job objective of Scaloid is to help write Android code in more elegant style. Although Scaloid doing good job on layout building, I haven't do much things about databases yet.

There are many DB frameworks for Scala. If you writing an app relying on DB extensively, I recommend to use one of them. But when you are using the built-in Sqlite just for storing some casual data, it is not a bad idea that using Android APIs for database access. The biggest problem in this case is that the code is not neat, because of its API architecture. Let's see a common example:

def readDailyPractice(query: String, params: Array[String]) = {
  var dailyTime: List[DailyPractice] = List()
  val c = getReadableDatabase.rawQuery(query, params)
  try {
    while (c.moveToNext()) {
      dailyTime = dailyTime :+ 
        new DailyPractice(c.getString(0), c.getString(1), c.getLong(2))
    } 
  } finally c.close()
  dailyTime
}

We have to use variable for the return object because the android.database.Cursor does not provide Scala-compatible iterator. Another clutter is close() call in try-finally section. This can be reduced in more functional-style:

def readDailyPractice(query: String, params: Array[String]) = 
  getReadableDatabase.rawQuery(query, params).closeAfter(_.map(c => 
    new DailyPractice(c.getString(0), c.getString(1), c.getLong(2)).toList)
  )

By declaring implicit def cursor2RichCursor(c: Cursor) = new RichCursor(c), or extending the trait org.scaloid.common.DatabaseImplicits to your class, The android.database.Cursor implicitly converted into RichCursor, which implements Iterable[Cursor]. Now, we can use many useful iterator methods such as map, foreach, and foldLeft.

I also introduce a function closeAfter[T](body: RichCursor => T):T that closes the cursor after evaluating its parameter.

One of the important thing that can be missed is to convert the mapped result into a List. Iterator.map returns another Iterator that evaluate the original iterator(RichCursor in this case) on demand. The implementation of Iterator.map looks like this:

def map[B](f: A => B): Iterator[B] = new AbstractIterator[B] {
  def hasNext = self.hasNext
  def next() = f(self.next())
}

So if we missed to convert the iterator to List or other form, newly created Iterator generated from the mapping will evaluate the Cursor that is already closed, and vomit the exception. This issue is too detailed, and very easy to miss at first. What we just need is iterate each rows and map a row with a domain object, and forget about cursor. So I created a wrapper function that does this mission simply:

def readDailyPractice(query: String, params: Array[String]) = 
  getReadableDatabase.rawQuery(query, params).orm(c => 
    new DailyPractice(c.getString(0), c.getString(1), c.getLong(2)))

As the name of the function implies, it does very simple form of Object-Relation-Mapping.

In other cases, we often need just one record from the query result. For example, getting count of something, or retrieving some column about a particular user. Even such a simple requirement, we have to write a verbose code as:

def readOneRecord(query: String, params: Array[String], default: String) = 
  try {
    val c = getReadableDatabase.rawQuery(query, params)
    if (c.moveToFirst()) c.getString(0) else default
  } finally c.close()

I created a helper method toString(defaultVal) for this purpose. We can rewrite the code above as:

def readOneRecord(query: String, params: Array[String], default: String) = 
  getReadableDatabase.rawQuery(query, params).toString(default)

Other accessors, toShort, toLong, toInt, toFloat, and toDouble is available as well.

All of these magic can be done by the implicitly converted class RichCursor. The source code of RichCursor is very simple.

class RichCursor(c: Cursor) extends Iterable[Cursor] {
  def iterator = new CursorIterator

  class CursorIterator extends Iterator[Cursor] {
    def hasNext = c.getPosition < c.getCount - 1

    def next() = {
      c.moveToNext()
      c
    }
  }

  def closeAfter[T](body: RichCursor => T) = try body(this) finally c.close()

  def orm[T](body: Cursor => T) = closeAfter(_.map(body).toList)

  def toLong(default: Long): Long = closeAfter(csr => if (c.moveToFirst()) c.getLong(0) else default)

  // definitions of toString, toShort... is straightforward
}

This class is also available on Scaloid 3.2.1

Thursday, November 28, 2013

Scaloid 3.0 is released

Today we are pleased to announce Scaloid 3.0. Some new feature highlights are as shown below:

Redesign of LocalServiceConnection

LocalServiceConnection is redesigned to use Option, and prevents possibility of NullPointerException, which was very frequent when accessing service with LocalServiceConnection.

This is a breaking change in source code level. Please migrate the code as introduced in a blog post below:
http://blog.scaloid.org/2013/11/redesign-of-localserviceconnection.html

Changed SImageView(r: Int) to SImageView(r: Drawable)

We believe that source level compatibility is not broken. However, rebuild is required because the parameter signature is changed.

https://github.com/pocorall/scaloid/commit/4f708eb4017b848a1f6ae5d542435b61781473fe

SPaint support

We added an initial attempt to embrace graphics feature of Android API. Please refer to this blog post for a design principle behind this addition.

https://github.com/pocorall/scaloid/commit/b0dc224935a9a4c2133d0a5404a72646f04d0215

For a maven project:
<dependency>
    <groupId>org.scaloid</groupId>
    <artifactId>scaloid_2.10</artifactId>
    <version>3.0-8</version>
</dependency>
or for an sbt project:
libraryDependencies += "org.scaloid" %% "scaloid" % "3.0-8"

Sunday, November 17, 2013

Usage driven design - choosing parameters

Scaloid is originally made to improve my daily life that codes Android apps. Although Scaloid relies on some core design principles that is behind of automatic wrapper generator, sometimes code can be improved from some heuristics. For example, I found that my code has several functions much like this:

def createMyPaint(): Paint = {
    val p = new Paint()
    p.setColor(0xffff6666)
    p.setTextSize(14.sp)
    p
}

Soon I added a wrapper class of android.graphics.Paint to Scaloid. Then the code above is improved as:

def createMyPaint() = SPaint().color(0xffff6666).textSize(14.sp)

All of my codes and a Google search reveals that setColor() is always called after a new Paint instance is initialized. It is very natural to think about a color when we tried to paint something. So I added it as the first parameter of the method SPaint.apply(); then the code can be rewritten as:

def createMyPaint() = SPaint(0xffff6666).textSize(14.sp)

We have reached the minimum code as possible.

This feature is shipped with Scaloid 3.0-M2. You can include this version of Scaloid into a maven project by:
<dependency>
    <groupId>org.scaloid</groupId>
    <artifactId>scaloid_2.10</artifactId>
    <version>3.0-8-M2</version>
</dependency>
or a sbt project by:
libraryDependencies += "org.scaloid" %% "scaloid" % "3.0-8-M2"

Friday, November 15, 2013

Redesign of LocalServiceConnection

For the current version of Scaloid, the type of LocalServiceConnection.service is [S <: LocalService]. This value is null if the service is not yet connected. So it is exposed to the possibility of NullPointerException. To avoid the exception, users must check that the service is connected, as shown below:

class MyService extends LocalService {
  def getNumber(): Int = 3
}

class Activity extends SActivity {
  val number = new LocalServiceConnection[MyService]

  def onButtonClick(v:View) {
    val num = if(number.connected) number.service.getNumber() else 0
    doSomethingWith(num)
  }
}

The problem is that the checking is very easy to forget. So I changed the type of LocalServiceConnection.service to Option[S], so that the possibility of NPE goes away. Moreover, I added some additional helper methods that decreases clutters from your code:

def onButtonClick(v:View) {
  val num = number(_.getNumber(), 0)
  doSomethingWith(num)
}

The method LocalServiceConnection.apply[T](f: S => T, defaultVal: T): T returns the result of the function f if the service is connected, and returns defaultVal otherwise. This is far more cleaner than the previous one.

If we don't want to return something, and therefore we don't have any default value, we can use it as:
def onButtonClick(v:View) {
  number(s => doSomethingWith(s.getNumber()))
}
We often tests some condition and evaluates different expression according to the condition:
def onButtonClick(v:View) {
  val result = if(number(_.getNumber(), 0) > 10) 
                 "10 < " + number(_.getNumber()) else "fail"
  doSomethingWith(result)
}
It can also be reduced to:
def onButtonClick(v:View) {
  val result = number(_.getNumber() > 10, "10 < " + _.getNumber(), "fail")
  doSomethingWith(result)
}

Because we have a breaking change, this feature will be shipped with Scaloid 3.0. You can evaluate this feature from the first milestone release. For a maven project:
<dependency>
    <groupId>org.scaloidscaloid_2.10</artifactId>
    <version>3.0-8-M1</version>
</dependency>
or for an sbt project:
libraryDependencies += "org.scaloid" %% "scaloid" % "3.0-8-M1"

Wednesday, November 13, 2013

Using scala.concurrent.future on Android

From Scala 2.10, a concurrent execution function scala.concurrent.ops.spawn is deprecated, replaced with scala.concurrent.future, and will be removed from Scala 2.11.

I rewrote my app with scala.concurrent.future as an official Scala documentation suggests:
import ExecutionContext.Implicits.global
future {
// ...
}

However, after I uploaded the app to Google Play, I received a ton of crash report:
java.lang.ExceptionInInitializerError
at scala.concurrent.impl.ExecutionContextImpl.createExecutorService(ExecutionContextImpl.scala:77)
at scala.concurrent.impl.ExecutionContextImpl.<init>(ExecutionContextImpl.scala:28)
at scala.concurrent.ExecutionContext$Implicits$.global$lzycompute(ExecutionContext.scala:63)
at scala.concurrent.ExecutionContext$Implicits$.global(ExecutionContext.scala:63)
at com.soundcorset.client.android.SoundcorsetService$Metronome.start(SoundcorsetService.scala:38)
...
Caused by: java.lang.Error: java.lang.NoSuchFieldException: parkBlocker
at scala.concurrent.forkjoin.ForkJoinPool.<clinit>(ForkJoinPool.java:2852)
... 20 more
Caused by: java.lang.NoSuchFieldException: parkBlocker
at java.lang.ClassCache.findFieldByName(ClassCache.java:510)
at java.lang.Class.getDeclaredField(Class.java:683)
at scala.concurrent.forkjoin.ForkJoinPool.<clinit>(ForkJoinPool.java:2847)
... 20 more

Scala library has its own copy of ForkJoinPool implementation, which includes dynamic invocations, that is parkBlocker in this case. Unfortunately, some Android devices does not have this method, so we've got this awful crash report.

The solution is very simple: Do not use ExecutionContext.Implicits.global. Declare a custom implicit ExecutionContext instead. For example:
implicit val exec = ExecutionContext.fromExecutor(
  new ThreadPoolExecutor(100, 100, 1000, TimeUnit.SECONDS,
    new LinkedBlockingQueue[Runnable]))

future {
// ...
}

This works perfect in production.