Skip to content
This repository has been archived by the owner on Nov 30, 2022. It is now read-only.

Commit

Permalink
! all: Replace AppContext and ActivityContext with ContextWrapper
Browse files Browse the repository at this point in the history
This implementation is more flexible (for example, it supports
widget creation in Services) and requires less boilerplate.
The WeakReference mechanism is still in place. See discussion in #54.
  • Loading branch information
stanch committed Apr 26, 2015
1 parent d27d2b3 commit 16cb506
Show file tree
Hide file tree
Showing 27 changed files with 238 additions and 222 deletions.
1 change: 0 additions & 1 deletion macroid-core/src/main/scala/macroid/Cakes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ private[macroid] trait FullDsl
with LayoutDsl with Tweaks with Snails
with ToastDsl with Loafs
with DialogDsl with Phrases
with Resources
with MediaQueries

object FullDsl extends FullDsl
69 changes: 49 additions & 20 deletions macroid-core/src/main/scala/macroid/Contexts.scala
Original file line number Diff line number Diff line change
@@ -1,41 +1,70 @@
package macroid

import android.app.Activity
import android.app.{Service, Activity, Application}
import android.content.Context
import scala.ref.WeakReference
import scala.annotation.implicitNotFound
import macroid.support.{ Fragment, FragmentApi }

@implicitNotFound("Could not find `AppContext`. If you are inside Activity or Fragment, extend Contexts[Activity] or Contexts[Fragment], otherwise pass an instance of `AppContext` from outside.") /** Global application context, which is safe to hold on to */
case class AppContext(app: Context) {
def get = app
}
@implicitNotFound("""Could not find a `ContextWrapper`.
If you are inside Activity, Fragment or Service, extend Contexts[Activity], Contexts[Fragment] or Contexts[Service],
otherwise pass an instance of `ContextWrapper` from outside.""")
/** A wrapper that contains two contexts:
* 1. the application context (which should be always alive)
* 2. the current context, usually Activity or Service
* (which is more specific, but may die and is stored as a weak reference)
*/
sealed trait ContextWrapper {
type C <: Context

def original: WeakReference[C]
def application: Context

@implicitNotFound("Could not find `ActivityContext`. If you are inside Activity or Fragment, extend Contexts[Activity] or Contexts[Fragment], otherwise pass an instance of `ActivityContext` from outside.") /** Activity context, stored as a WeakReference */
case class ActivityContext(activity: WeakReference[Activity]) {
def get = activity()
def getOriginal: C = original.get.get
def bestAvailable: Context = original.get getOrElse application
}
object ActivityContext {
def apply(activity: Activity) = new ActivityContext(WeakReference(activity))

case class ActivityContextWrapper(original: WeakReference[Activity], application: Context)
extends ContextWrapper { type C = Activity }

case class ServiceContextWrapper(original: WeakReference[Service], application: Context)
extends ContextWrapper { type C = Service }

case class ApplicationContextWrapper(original: WeakReference[Application], application: Context)
extends ContextWrapper { type C = Application }

object ContextWrapper {
def apply(activity: Activity): ActivityContextWrapper =
ActivityContextWrapper(WeakReference(activity), activity.getApplicationContext)

def apply(app: Application): ApplicationContextWrapper =
ApplicationContextWrapper(WeakReference(app), app)

def apply(service: Service): ServiceContextWrapper =
ServiceContextWrapper(WeakReference(service), service.getApplicationContext)

def apply[F](fragment: F)(implicit fragmentImpl: Fragment[F]): ActivityContextWrapper =
ContextWrapper(fragmentImpl.activity(fragment))
}

@implicitNotFound("Could not find `FragmentManagerContext[${F}, ${M}]`. If you are inside Activity or Fragment, extend Contexts[Activity] or Contexts[Fragment], otherwise pass an instance of `FragmentManagerContext` from outside. Please note that for support fragments you need to extends Contexts[FragmentActivity]") /** FragmentManager context */
@implicitNotFound("""Could not find `FragmentManagerContext[${F}, ${M}]`.
If you are inside Activity or Fragment, extend Contexts[Activity] or Contexts[Fragment],
otherwise pass an instance of `FragmentManagerContext` from outside.
Please note that for support fragments you need to extends Contexts[FragmentActivity]""")
/** FragmentManager context */
case class FragmentManagerContext[-F, M](manager: M)(implicit val fragmentApi: FragmentApi[F, M, _]) {
def get = manager
}

trait Contexts[X] { self: X
implicit def activityAppContext(implicit activity: X <:< Activity) =
AppContext(activity(self).getApplicationContext)

implicit def activityActivityContext(implicit activity: X <:< Activity) =
ActivityContext(activity(self))
implicit def activityContextWrapper(implicit activity: X <:< Activity): ActivityContextWrapper =
ContextWrapper(activity(self))

implicit def fragmentAppContext(implicit fragment: Fragment[X]) =
AppContext(fragment.activity(self).getApplicationContext)
implicit def fragmentContextWrapper(implicit fragment: Fragment[X]): ActivityContextWrapper =
ContextWrapper(self)(fragment)

implicit def fragmentActivityContext(implicit fragment: Fragment[X]) =
ActivityContext(fragment.activity(self))
implicit def serviceContextWrapper(implicit service: X <:< Service): ServiceContextWrapper =
ContextWrapper(service(self))

implicit def activityManagerContext[M, F, A >: X](implicit fragmentApi: FragmentApi[F, M, A]) =
FragmentManagerContext[F, M](fragmentApi.activityManager(self))
Expand Down
12 changes: 6 additions & 6 deletions macroid-core/src/main/scala/macroid/DialogDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ case class Phrase(f: AlertDialog.Builder ⇒ Unit) {
private[macroid] trait DialogBuilding {
/** A helper class to provide different ways of building a dialog */
class DialogBuilder[A](theme: Option[Int]) {
private def builder(implicit ctx: ActivityContext) =
theme.fold(new AlertDialog.Builder(ctx.get))(t new AlertDialog.Builder(ctx.get, t))
private def builder(implicit ctx: ContextWrapper) =
theme.fold(new AlertDialog.Builder(ctx.getOriginal))(t new AlertDialog.Builder(ctx.getOriginal, t))

/** Create a dialog with the specified view */
def apply(view: Ui[View])(implicit ctx: ActivityContext): Ui[AlertDialog.Builder] =
def apply(view: Ui[View])(implicit ctx: ContextWrapper): Ui[AlertDialog.Builder] =
view.map(v builder.setView(v))

/** Create a dialog with the specified message */
def apply(message: CharSequence)(implicit ctx: ActivityContext): Ui[AlertDialog.Builder] =
def apply(message: CharSequence)(implicit ctx: ContextWrapper): Ui[AlertDialog.Builder] =
Ui(builder.setMessage(message))

/** Create a dialog with the specified item list and click handler */
def apply(items: Array[CharSequence])(handler: OnClickListener)(implicit ctx: ActivityContext): Ui[AlertDialog.Builder] =
def apply(items: Array[CharSequence])(handler: OnClickListener)(implicit ctx: ContextWrapper): Ui[AlertDialog.Builder] =
Ui(builder.setItems(items, handler))

/** Create a dialog with the specified ListAdapter and click handler */
def apply(adapter: ListAdapter)(handler: OnClickListener)(implicit ctx: ActivityContext): Ui[AlertDialog.Builder] =
def apply(adapter: ListAdapter)(handler: OnClickListener)(implicit ctx: ContextWrapper): Ui[AlertDialog.Builder] =
Ui(builder.setAdapter(adapter, handler))
}

Expand Down
18 changes: 9 additions & 9 deletions macroid-core/src/main/scala/macroid/FragmentBuilding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import macroid.contrib.Layouts.RootFrameLayout
import macroid.support.Fragment

/** A fragment builder proxy */
case class FragmentBuilder[F](constructor: Ui[F], arguments: Bundle)(implicit ctx: ActivityContext, fragment: Fragment[F]) {
case class FragmentBuilder[F](constructor: Ui[F], arguments: Bundle)(implicit ctx: ContextWrapper, fragment: Fragment[F]) {
import Searching._
import Bundles._

Expand All @@ -29,7 +29,7 @@ case class FragmentBuilder[F](constructor: Ui[F], arguments: Bundle)(implicit ct
f.getOrElse {
managerCtx.fragmentApi.addFragment(managerCtx.get, id, tag, factory.get)
}
new RootFrameLayout(ctx.get) {
new RootFrameLayout(ctx.getOriginal) {
setId(id)
}
}
Expand All @@ -42,32 +42,32 @@ private[macroid] trait FragmentBuilding extends Bundles {
/**
* Fragment builder. To create a fragment, newInstance() is called, and if that fails, class constructor is used.
*/
def fragment[F](implicit ctx: ActivityContext, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentImpl[F]
def fragment[F](implicit ctx: ContextWrapper, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentImpl[F]

/**
* Fragment builder. To create a fragment, newInstance() is called, and if that fails, class constructor is used.
* (This is an alias for `fragment`.)
*/
def f[F](implicit ctx: ActivityContext, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentImpl[F]
def f[F](implicit ctx: ContextWrapper, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentImpl[F]

/**
* Fragment builder. `newInstanceArgs` are passed to newInstance, if any.
* Without arguments, newInstance() is called, and if that fails, class constructor is used.
*/
def fragment[F](newInstanceArgs: Any*)(implicit ctx: ActivityContext, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentArgImpl[F]
def fragment[F](newInstanceArgs: Any*)(implicit ctx: ContextWrapper, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentArgImpl[F]

/**
* Fragment builder. `newInstanceArgs` are passed to newInstance, if any.
* Without arguments, newInstance() is called, and if that fails, class constructor is used.
* (This is an alias for `fragment`.)
*/
def f[F](newInstanceArgs: Any*)(implicit ctx: ActivityContext, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentArgImpl[F]
def f[F](newInstanceArgs: Any*)(implicit ctx: ContextWrapper, fragment: Fragment[F]): FragmentBuilder[F] = macro fragmentArgImpl[F]
}

object FragmentBuilding extends FragmentBuilding

object FragmentBuildingMacros {
def instFrag[F: c.WeakTypeTag](c: MacroContext)(args: Seq[c.Expr[Any]], ctx: c.Expr[ActivityContext]) = {
def instFrag[F: c.WeakTypeTag](c: MacroContext)(args: Seq[c.Expr[Any]], ctx: c.Expr[ContextWrapper]) = {
import c.universe._

scala.util.Try {
Expand All @@ -82,13 +82,13 @@ object FragmentBuildingMacros {
}
}

def fragmentImpl[F: c.WeakTypeTag](c: MacroContext)(ctx: c.Expr[ActivityContext], fragment: c.Expr[Fragment[F]]) = {
def fragmentImpl[F: c.WeakTypeTag](c: MacroContext)(ctx: c.Expr[ContextWrapper], fragment: c.Expr[Fragment[F]]) = {
import c.universe._
val constructor = instFrag(c)(Seq(), ctx)
c.Expr[FragmentBuilder[F]](q"_root_.macroid.FragmentBuilder(_root_.macroid.Ui($constructor), new _root_.android.os.Bundle)($ctx, $fragment)")
}

def fragmentArgImpl[F: c.WeakTypeTag](c: MacroContext)(newInstanceArgs: c.Expr[Any]*)(ctx: c.Expr[ActivityContext], fragment: c.Expr[Fragment[F]]) = {
def fragmentArgImpl[F: c.WeakTypeTag](c: MacroContext)(newInstanceArgs: c.Expr[Any]*)(ctx: c.Expr[ContextWrapper], fragment: c.Expr[Fragment[F]]) = {
import c.universe._
val constructor = instFrag(c)(newInstanceArgs, ctx)
c.Expr[FragmentBuilder[F]](q"_root_.macroid.FragmentBuilder(_root_.macroid.Ui($constructor), new _root_.android.os.Bundle)($ctx, $fragment)")
Expand Down
24 changes: 12 additions & 12 deletions macroid-core/src/main/scala/macroid/LayoutBuilding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,22 @@ private[macroid] trait LayoutBuilding {
import LayoutBuildingMacros._

/** Define a widget */
def widget[W <: View](implicit ctx: ActivityContext): Ui[W] = macro widgetImpl[W]
def widget[W <: View](implicit ctx: ContextWrapper): Ui[W] = macro widgetImpl[W]

/** Define a widget (an alias for `widget`) */
def w[W <: View](implicit ctx: ActivityContext): Ui[W] = macro widgetImpl[W]
def w[W <: View](implicit ctx: ContextWrapper): Ui[W] = macro widgetImpl[W]

/** Define a widget, supplying additional arguments */
def widget[W <: View](args: Any*)(implicit ctx: ActivityContext): Ui[W] = macro widgetArgImpl[W]
def widget[W <: View](args: Any*)(implicit ctx: ContextWrapper): Ui[W] = macro widgetArgImpl[W]

/** Define a widget, supplying additional arguments (an alias for `widget`) */
def w[W <: View](args: Any*)(implicit ctx: ActivityContext): Ui[W] = macro widgetArgImpl[W]
def w[W <: View](args: Any*)(implicit ctx: ContextWrapper): Ui[W] = macro widgetArgImpl[W]

/** Define a layout */
def layout[L <: ViewGroup](children: Ui[View]*)(implicit ctx: ActivityContext): Ui[L] = macro layoutUiImpl[L]
def layout[L <: ViewGroup](children: Ui[View]*)(implicit ctx: ContextWrapper): Ui[L] = macro layoutUiImpl[L]

/** Define a layout (an alias for `layout`) */
def l[L <: ViewGroup](children: Ui[View]*)(implicit ctx: ActivityContext): Ui[L] = macro layoutUiImpl[L]
def l[L <: ViewGroup](children: Ui[View]*)(implicit ctx: ContextWrapper): Ui[L] = macro layoutUiImpl[L]

/** Define a slot */
def slot[W <: View]: Option[W] = None
Expand All @@ -33,19 +33,19 @@ private[macroid] trait LayoutBuilding {
object LayoutBuilding extends LayoutBuilding

object LayoutBuildingMacros {
def widgetImpl[W <: View: c.WeakTypeTag](c: MacroContext)(ctx: c.Expr[ActivityContext]): c.Expr[W] = {
def widgetImpl[W <: View: c.WeakTypeTag](c: MacroContext)(ctx: c.Expr[ContextWrapper]): c.Expr[W] = {
import c.universe._
c.Expr[W](q"_root_.macroid.Ui(new ${weakTypeOf[W]}($ctx.get))")
c.Expr[W](q"_root_.macroid.Ui(new ${weakTypeOf[W]}($ctx.getOriginal))")
}

def widgetArgImpl[W <: View: c.WeakTypeTag](c: MacroContext)(args: c.Expr[Any]*)(ctx: c.Expr[ActivityContext]): c.Expr[W] = {
def widgetArgImpl[W <: View: c.WeakTypeTag](c: MacroContext)(args: c.Expr[Any]*)(ctx: c.Expr[ContextWrapper]): c.Expr[W] = {
import c.universe._
c.Expr[W](q"_root_.macroid.Ui(new ${weakTypeOf[W]}($ctx.get, ..$args))")
c.Expr[W](q"_root_.macroid.Ui(new ${weakTypeOf[W]}($ctx.getOriginal, ..$args))")
}

def layoutUiImpl[L <: ViewGroup: c.WeakTypeTag](c: MacroContext)(children: c.Expr[Ui[View]]*)(ctx: c.Expr[ActivityContext]): c.Expr[L] = {
def layoutUiImpl[L <: ViewGroup: c.WeakTypeTag](c: MacroContext)(children: c.Expr[Ui[View]]*)(ctx: c.Expr[ContextWrapper]): c.Expr[L] = {
import c.universe._
val untypechecked = children.map(ch c.resetLocalAttrs(ch.tree))
c.Expr[L](q"_root_.macroid.Ui.sequence(..$untypechecked).map(ch ⇒ new ${weakTypeOf[L]}($ctx.get) { ch.foreach(this.addView) })")
c.Expr[L](q"_root_.macroid.Ui.sequence(..$untypechecked).map(ch ⇒ new ${weakTypeOf[L]}($ctx.getOriginal) { ch.foreach(this.addView) })")
}
}
46 changes: 23 additions & 23 deletions macroid-core/src/main/scala/macroid/MediaQueries.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,30 @@ object MediaQuery {
}

private[macroid] trait MediaQueryEssentials {
protected def displayMetrics(implicit ctx: AppContext) = {
val display = ctx.get.getSystemService(Context.WINDOW_SERVICE).asInstanceOf[WindowManager].getDefaultDisplay
protected def displayMetrics(implicit ctx: ContextWrapper) = {
val display = ctx.application.getSystemService(Context.WINDOW_SERVICE).asInstanceOf[WindowManager].getDefaultDisplay
val metrics = new DisplayMetrics
display.getMetrics(metrics)
metrics
}
}

private[macroid] trait DensityQueries extends MediaQueryEssentials {
def ldpi(implicit ctx: AppContext) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_LOW)
def mdpi(implicit ctx: AppContext) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_MEDIUM)
def hdpi(implicit ctx: AppContext) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_HIGH)
def xhdpi(implicit ctx: AppContext) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_XHIGH)
def ldpi(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_LOW)
def mdpi(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_MEDIUM)
def hdpi(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_HIGH)
def xhdpi(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.densityDpi == DisplayMetrics.DENSITY_XHIGH)
}

private[macroid] trait OrientationQueries {
def portrait(implicit ctx: AppContext) =
MediaQuery(ctx.get.getResources.getConfiguration.orientation == Configuration.ORIENTATION_PORTRAIT)
def landscape(implicit ctx: AppContext) =
MediaQuery(ctx.get.getResources.getConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE)
def portrait(implicit ctx: ContextWrapper) =
MediaQuery(ctx.application.getResources.getConfiguration.orientation == Configuration.ORIENTATION_PORTRAIT)
def landscape(implicit ctx: ContextWrapper) =
MediaQuery(ctx.application.getResources.getConfiguration.orientation == Configuration.ORIENTATION_LANDSCAPE)
}

private[macroid] trait DisplayUnits extends MediaQueryEssentials {
implicit class Units[A](v: A)(implicit ctx: AppContext, numeric: Numeric[A]) {
implicit class Units[A](v: A)(implicit ctx: ContextWrapper, numeric: Numeric[A]) {
import Numeric.Implicits.infixNumericOps
/** Using pixels is strictly discouraged! */
def px = v.toInt()
Expand All @@ -57,31 +57,31 @@ private[macroid] trait DisplayUnits extends MediaQueryEssentials {

private[macroid] trait SizeQueries extends MediaQueryEssentials {
/** Width is at least `v` */
def minWidth(v: Int)(implicit ctx: AppContext) = MediaQuery(displayMetrics.widthPixels >= v)
def minWidth(v: Int)(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.widthPixels >= v)
/** Width is at least `v`(alias for `minWidth`) */
def widerThan(v: Int)(implicit ctx: AppContext) = minWidth(v)
def widerThan(v: Int)(implicit ctx: ContextWrapper) = minWidth(v)
/** Width is at most `v` */
def maxWidth(v: Int)(implicit ctx: AppContext) = MediaQuery(displayMetrics.widthPixels <= v)
def maxWidth(v: Int)(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.widthPixels <= v)
/** Width is at most `v` (alias for `maxWidth`) */
def narrowerThan(v: Int)(implicit ctx: AppContext) = maxWidth(v)
def narrowerThan(v: Int)(implicit ctx: ContextWrapper) = maxWidth(v)

/** Height is at least `v` */
def minHeight(v: Int)(implicit ctx: AppContext) = MediaQuery(displayMetrics.heightPixels >= v)
def minHeight(v: Int)(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.heightPixels >= v)
/** Height is at least `v` (alias for `minHeight`) */
def higherThan(v: Int)(implicit ctx: AppContext) = minHeight(v)
def higherThan(v: Int)(implicit ctx: ContextWrapper) = minHeight(v)
/** Height is at most `v` */
def maxHeight(v: Int)(implicit ctx: AppContext) = MediaQuery(displayMetrics.heightPixels <= v)
def maxHeight(v: Int)(implicit ctx: ContextWrapper) = MediaQuery(displayMetrics.heightPixels <= v)
/** Height is at most `v` (alias for `maxHeight`) */
def lowerThan(v: Int)(implicit ctx: AppContext) = maxHeight(v)
def lowerThan(v: Int)(implicit ctx: ContextWrapper) = maxHeight(v)

/** Both sides are at least `v` */
def minSide(v: Int)(implicit ctx: AppContext) = minWidth(v) & minHeight(v)
def minSide(v: Int)(implicit ctx: ContextWrapper) = minWidth(v) & minHeight(v)
/** Both sides are at least `v` (alias for `minSide`) */
def biggerThan(v: Int)(implicit ctx: AppContext) = minSide(v)
def biggerThan(v: Int)(implicit ctx: ContextWrapper) = minSide(v)
/** Both sides are at most `v` */
def maxSide(v: Int)(implicit ctx: AppContext) = maxWidth(v) & maxHeight(v)
def maxSide(v: Int)(implicit ctx: ContextWrapper) = maxWidth(v) & maxHeight(v)
/** Both sides are at most `v` (alias for `maxSide`) */
def smallerThan(v: Int)(implicit ctx: AppContext) = maxSide(v)
def smallerThan(v: Int)(implicit ctx: ContextWrapper) = maxSide(v)
}

private[macroid] trait MediaQueries
Expand Down
26 changes: 0 additions & 26 deletions macroid-core/src/main/scala/macroid/Resources.scala

This file was deleted.

Loading

0 comments on commit 16cb506

Please sign in to comment.