import sbt._
import scala.xml._
trait TypedResources extends AndroidProject {
def managedScalaPath = "src_managed" / "main" / "scala"
def typedResource = managedScalaPath / "TR.scala"
abstract override def mainSourceRoots = super.mainSourceRoots +++ managedScalaPath
def layoutResources = mainResPath / "layout" ** "*.xml"
override def compileAction = super.compileAction dependsOn generateTypedResources
override def cleanAction = super.cleanAction dependsOn cleanTask(managedScalaPath)
override def watchPaths = super.watchPaths +++ layoutResources
lazy val generateTypedResources = fileTask(typedResource from layoutResources) {
val Id = """@\+id/(.*)""".r
val androidJarLoader = ClasspathUtilities.toLoader(androidJarPath)
val resources = layoutResources.get.flatMap { path =>
XML.loadFile(path.asFile).descendant_or_self flatMap { node =>
node.attribute("http://schemas.android.com/apk/res/android", "id") flatMap {
_.firstOption map { _.text } flatMap {
case Id(id) if node.label.contains('.') => Some(id, node.label)
case Id(id) => try { Some(id,
androidJarLoader.loadClass("android.widget." + node.label).getName
) } catch { case _ => None }
case _ => None
}
}
}
}.foldLeft(Map.empty[String, String]) {
case (m, (k, v)) =>
m.get(k).foreach { v0 =>
if (v0 != v) log.warn("Resource id '%s' mapped to %s and %s" format (k, v0, v))
}
m + (k -> v)
}
FileUtilities.write(typedResource.asFile,
""" |package %s
|import android.app.Activity
|import android.view.View
|
|case class TypedResource[T](id: Int)
|object TR {
|%s
|}
|trait TypedViewHolder {
| def view: View
| def findView[T](tr: TypedResource[T]) = view.findViewById(tr.id).asInstanceOf[T]
|}
|trait TypedView extends View { def view = this }
|trait TypedActivityHolder {
| def activity: Activity
| def findView[T](tr: TypedResource[T]) = activity.findViewById(tr.id).asInstanceOf[T]
|}
|trait TypedActivity extends Activity with TypedActivityHolder { def activity = this }
|object TypedResource {
| implicit def view2typed(v: View) = new TypedViewHolder { def view = v }
| implicit def activity2typed(act: Activity) = new TypedActivityHolder { def activity = act }
|}
|""".stripMargin.format(
manifestPackage, resources map { case (id, classname) =>
" val %s = TypedResource[%s](R.id.%s)".format(id, classname, id)
} mkString "\n"
), log
)
None
}
}