All Downloads are FREE. Search and download functionalities are using the official Maven repository.

iota.Contexts.scala Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC3
Show newest version
package iota

import android.app.Activity
import android.content.{Context => AndroidContext}
import android.net.nsd.NsdManager
import android.telephony.TelephonyManager
import scala.annotation.implicitNotFound
import scala.reflect.ClassTag
import scala.reflect.macros.{Context => MacroContext}

/**
 * @author pfnguyen
 */
@implicitNotFound("Unable to find a service constant for ${T},\n" +
  "add the following implicit value to your code if this is not a mistake:\n    " +
  "'implicit val `systemService for ${T}` =\n      " +
  "SystemService[${T}](GET_SERVICE_CONSTANT)`'")
case class SystemService[T](name: String) extends AnyVal
private[iota] trait Contexts {
  /** pull a context out of "thin air", checks for Activity, Fragment and WithContext */
  implicit def materializeContext: AndroidContext = macro ContextMacro.materializeContextImpl

  implicit def materializeActivity: Activity = macro ContextMacro.materializeActivityImpl

  /** find a strongly-typed view.
    * will fail to compile if id(xxx) is not used prior in the source
    */
  @deprecated("Use the view holder pattern for better compile-time safety", "0.9.2")
  def findView[A <: android.view.View : ViewIdType : ClassTag](id: Int)(implicit activity: Activity): A = {
    val v = activity.findViewById(id).asInstanceOf[A]
    if (v == null) throw new NullPointerException(s"view $id not found")
    v
  }

  /** find a strongly-typed view.
    * will fail to compile if id(xxx) is not used prior in the source
    */
  @deprecated("Use the view holder pattern for better compile-time safety", "0.9.2")
  def findViewOption[A <: android.view.View : ViewIdType : ClassTag](id: Int)(implicit activity: Activity): Option[A] =
    Option(activity.findViewById(id).asInstanceOf[A])

  implicit val `nsd system service` =
    SystemService[NsdManager](AndroidContext.NSD_SERVICE)
  implicit val `telephony system service` =
    SystemService[TelephonyManager](AndroidContext.TELEPHONY_SERVICE)

  implicit def materializeSystemService[T]: SystemService[T] = macro ContextMacro.materializeSystemServiceImpl[T]
  /** type-safe retrieval of system service objects.
    * e.g. `systemService[NotificationManager]`
    */
  @inline final def systemService[T](implicit s: SystemService[T], context: AndroidContext): T =
    context.getSystemService(s.name).asInstanceOf[T]
}
private[iota] object ContextMacro {
  private[this] val TYPES =
    "android.app.Context" ::
    "android.app.Fragment" ::
    "android.view.View" ::
    "android.support.v4.app.Fragment" ::
    "iota.WithContext" ::
    Nil
  def materializeContextImpl(c: MacroContext): c.Expr[AndroidContext] = {
    import c.universe._
    val supportFragment = util.Try(rootMirror.staticClass("android.support.v4.app.Fragment")).toOption
    val classType = c.enclosingClass.symbol.asType.toType

    def dfs(body: c.Tree, pos: c.Position, path: List[c.Tree]): List[c.Tree] =
      if (body.pos == pos) path.collect { case a@ClassDef(_,_,_,_) => a }
      else body.children.flatMap { child => dfs(child, pos, body :: path) }

    def expandTree(tpe: Type): Option[c.Tree] = {
      if (tpe <:< c.weakTypeOf[AndroidContext]) {
        Option(This(tpe.typeSymbol))
      } else if (tpe <:< c.weakTypeOf[HasContext]) {
        Option(Select(This(tpe.typeSymbol), newTermName("context")))
      } else if (tpe <:< c.weakTypeOf[HasActivity]) {
        Option(Select(This(tpe.typeSymbol), newTermName("activity")))
      } else if (tpe <:< c.weakTypeOf[android.view.View]) {
        Option(Apply(Select(This(tpe.typeSymbol), newTermName("getContext")), Nil))
      } else if (tpe <:< c.weakTypeOf[android.app.Fragment]) {
        Option(Apply(Select(This(tpe.typeSymbol), newTermName("getActivity")), Nil))
      } else if (supportFragment.exists(tpe <:< _.toType)) {
        Option(Apply(Select(This(tpe.typeSymbol), newTermName("getActivity")), Nil))
      } else {
        None
      }
    }

    def error = c.abort(c.enclosingPosition,
      s"$classType does not extend any of:\n    ${TYPES mkString "\n    "}")

    c.Expr(expandTree(classType).getOrElse {
      val paths = dfs(c.enclosingUnit.body, c.enclosingPosition, Nil)
      paths.find(_.symbol.asType.toType <:< c.weakTypeOf[AndroidContext]).fold(
        error
      )(t => expandTree(t.symbol.asType.toType).getOrElse (error))
    })
  }

  def materializeActivityImpl(c: MacroContext): c.Expr[Activity] = {
    import c.universe._
    val supportFragment = util.Try(rootMirror.staticClass("android.support.v4.app.Fragment")).toOption
    val classType = c.enclosingClass.symbol.asType.toType

    def dfs(body: c.Tree, pos: c.Position, path: List[c.Tree]): List[c.Tree] =
      if (body.pos == pos) path.collect { case a@ClassDef(_,_,_,_) => a }
      else body.children.flatMap { child => dfs(child, pos, body :: path) }

    def expandTree(tpe: Type): Option[c.Tree] = {
      if (tpe <:< c.weakTypeOf[Activity]) {
        Option(This(tpe.typeSymbol))
      } else if (tpe <:< c.weakTypeOf[HasActivity]) {
        Option(Select(This(tpe.typeSymbol), newTermName("activity")))
      } else if (tpe <:< c.weakTypeOf[android.app.Fragment]) {
        Option(Apply(Select(This(tpe.typeSymbol), newTermName("getActivity")), Nil))
      } else if (supportFragment.exists(tpe <:< _.toType)) {
        Option(Apply(Select(This(tpe.typeSymbol), newTermName("getActivity")), Nil))
      } else {
        None
      }
    }

    def error = c.abort(c.enclosingPosition,
      s"$classType does not extend any of:\n    ${TYPES mkString "\n    "}")

    c.Expr(expandTree(classType).getOrElse {
      val paths = dfs(c.enclosingUnit.body, c.enclosingPosition, Nil)
      paths.find(_.symbol.asType.toType <:< c.weakTypeOf[AndroidContext]).fold(
        error
      )(t => expandTree(t.symbol.asType.toType).getOrElse (error))
    })
  }

  private[this] lazy val SERVICE_CONSTANTS = {
    val fields = classOf[AndroidContext].getDeclaredFields filter {
      _.getName endsWith "_SERVICE"
    }
    fields map { f =>
      val v = f.get(null).toString
      v.replaceAll("_", "") -> v
    } toSeq
  }
  def materializeSystemServiceImpl[T: c.WeakTypeTag](c: MacroContext): c.Expr[SystemService[T]] = {
    import c.universe._

    val tpe = weakTypeOf[T]
    val candidates = SERVICE_CONSTANTS filter (tpe.toString.toLowerCase contains _._1)
    val service = ("" /: candidates) { (a, b) =>
      if (a.length > b._2.length) a else b._2
    }

    if (service.isEmpty)
      c.abort(c.enclosingPosition, s"No service constant found for $tpe")
    c.Expr[SystemService[T]](Apply(TypeApply(
      Select(reify(iota.SystemService).tree, newTermName("apply")),
      List(TypeTree(tpe))
    ), List(Literal(Constant(service)))))
  }
}

/** When a `android.content.Context` can't be found automatically using
  * the implicits in `iota._` or `iota.std.Contexts._` implement this trait
  * to help the implicit out
  */
trait HasContext {
  def context: AndroidContext
}

/** When an `android.app.Activity` can't be found automatically using
  * the implicits in `iota._` or `iota.std.Contexts._` implement this trait
  * to help the implicit out
  */
trait HasActivity {
  def activity: Activity
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy