Fullscreen

Scala on Android

Who am I?

Geoffroy Couprie

Freelance in software security

VLC developer

Scala and Android, why?

The power of Scala

  button.setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
      //code here
    }
  }

Implicits

object Helpers {
  implicit def onClick(handler: View => Unit):View.OnClickListener = 
    new View.OnClickListener() {
    override def onClick(source: View) = handler(source)
    }

  implicit def onClick2(handler: => Unit):View.OnClickListener = 
    new View.OnClickListener() {
      override def onClick(source: View) = handler
    }
}

[...]

button.setOnClickListener((source:View) => {
  // code here
})

//Even better:

button.setOnClickListener {
  // code here
}

Activity code is not reusable

public class MainActivity extends Activity {
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_root, menu);
    return true;
  }
}

public class OtherActivity extends Activity {
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_root, menu);
    return true;
  }
}

Traits

trait Menu extends Activity {
  override def onCreateOptionsMenu(menu: Menu) : Boolean = {
    getMenuInflater().inflate(R.menu.activity_root, menu)
    true
  }
}

[...]

//Use it everywhere
class MainActivity extends Activity with Menu {
  ...
}

NULL POINTER EXCEPTION /o/

String   locProvider = LocationManager.GPS_PROVIDER;
Location loc         = locManager.getLastKnownLocation(locProvider);
int      satellites  = 0;
Bundle extras        = loc.getExtras();
int satellites       = extras.getInt("satellites");

Option

String   locProvider = LocationManager.GPS_PROVIDER;
Location loc         = locManager.getLastKnownLocation(locProvider);
int      satellites  = 0;
if(loc != null) {
  Bundle extras = loc.getExtras();
  if(extras != null) {
    satellites = extras.getInt("satellites");
  }
}

VS

val prov   = LocationManager.GPS_PROVIDER
val loc    = Option( locManager.getLastKnownLocation( prov ) )
val extras = loc.map{_.getExtras()}
val nb     = extras.map{_.getInt("satellites")}.getOrElse(0)

private class MyTask extends AsyncTask<String, Integer, Integer> {
  protected Integer doInBackground(String... urls) {
  }

  protected void onProgressUpdate(Integer... progress) {
  }

  protected void onPostExecute(Integer res) {
  }
}

Asynchronous tasks

import scala.concurrent._
import ExecutionContext.Implicits.global

...
future {
  val conn = url.openConnection.asInstanceOf[HttpURLConnection]
  val in   = new BufferedInputStream(conn.getInputStream())
  val res  = scala.io.Source.fromInputStream(in).mkString()
  conn.disconnect()
  res
} onSuccess {
  ...
}

Futures \o/

Cool libraries!

Rapture

import _root_.android.util.Log
import scala.concurrent._
import ExecutionContext.Implicits.global
import rapture.io._

...

future {
  val res = (Http / "geoffroycouprie.com").slurp[Char]
  Log.v("myapp", res)
}

implicit def toRunnable[A](f: => A):Runnable =
  new Runnable() { def run() = f }

...

future {
  (Http / "geoffroycouprie.com").slurp[Char]
} onSuccess { value =>
  runOnUiThread {
    Toast.makeText(this, value, Toast.LENGTH_LONG).show()
  }
}

Scaloid

new SVerticalLayout {
  STextView("Sign in").textSize(24.5 sp).<<.marginBottom(25 dip).>>
  STextView("ID")
  val userId = SEditText()
  STextView("Password")
  val pass = SEditText() inputType TEXT_PASSWORD
  SButton("Sign in", signin(userId.text, pass.text))
  this += new SLinearLayout {
    SButton("Help", openUri("http://help.example.com"))
    SButton("Sign up", openUri("http://signup.example.com"))
  }
}.padding(20 dip)

Installation

SBT and giter8

$g8 Geal/android-app

package [my.android.project]: com.scalaonandroid.testapp
name [MyAndroidProject]: TestApp
main_activity [MainActivity]:
scala_version [2.10.1]:
api_level [10]:
useProguard [true]:
scalatest_version [1.9.1]:

Template applied in ./test-app

$cd test-app && sbt android:install-device

Eclipse

Software sources:

Proguard is slow

$ time sbt android:install-device

[...]

real    5m23.410s
user    5m6.835s
sys     0m5.469s

Rooted smartphone

-> install libraries on the phone

emulator

-> HAXM or Android x86 on VirtualBox

Use cases

Android API interaction

Testable code

web services

Rapture, etc