diff --git a/macroid-core/src/main/scala/macroid/Cakes.scala b/macroid-core/src/main/scala/macroid/Cakes.scala index c0f2b87..b6d0d15 100644 --- a/macroid-core/src/main/scala/macroid/Cakes.scala +++ b/macroid-core/src/main/scala/macroid/Cakes.scala @@ -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 diff --git a/macroid-core/src/main/scala/macroid/Contexts.scala b/macroid-core/src/main/scala/macroid/Contexts.scala index 5c8e41e..5c310a8 100644 --- a/macroid-core/src/main/scala/macroid/Contexts.scala +++ b/macroid-core/src/main/scala/macroid/Contexts.scala @@ -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)) diff --git a/macroid-core/src/main/scala/macroid/DialogDsl.scala b/macroid-core/src/main/scala/macroid/DialogDsl.scala index 5d48353..a63a4a2 100644 --- a/macroid-core/src/main/scala/macroid/DialogDsl.scala +++ b/macroid-core/src/main/scala/macroid/DialogDsl.scala @@ -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)) } diff --git a/macroid-core/src/main/scala/macroid/FragmentBuilding.scala b/macroid-core/src/main/scala/macroid/FragmentBuilding.scala index cff8278..0d05d26 100644 --- a/macroid-core/src/main/scala/macroid/FragmentBuilding.scala +++ b/macroid-core/src/main/scala/macroid/FragmentBuilding.scala @@ -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._ @@ -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) } } @@ -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 { @@ -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)") diff --git a/macroid-core/src/main/scala/macroid/LayoutBuilding.scala b/macroid-core/src/main/scala/macroid/LayoutBuilding.scala index 351c1a1..b3eb159 100644 --- a/macroid-core/src/main/scala/macroid/LayoutBuilding.scala +++ b/macroid-core/src/main/scala/macroid/LayoutBuilding.scala @@ -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 @@ -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) })") } } diff --git a/macroid-core/src/main/scala/macroid/MediaQueries.scala b/macroid-core/src/main/scala/macroid/MediaQueries.scala index bb0b716..fe209be 100644 --- a/macroid-core/src/main/scala/macroid/MediaQueries.scala +++ b/macroid-core/src/main/scala/macroid/MediaQueries.scala @@ -21,8 +21,8 @@ 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 @@ -30,21 +30,21 @@ private[macroid] trait MediaQueryEssentials { } 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() @@ -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 diff --git a/macroid-core/src/main/scala/macroid/Resources.scala b/macroid-core/src/main/scala/macroid/Resources.scala deleted file mode 100644 index f435992..0000000 --- a/macroid-core/src/main/scala/macroid/Resources.scala +++ /dev/null @@ -1,26 +0,0 @@ -package macroid - -import scala.language.dynamics -import scala.language.experimental.macros -import scala.reflect.macros.{ Context ⇒ MacroContext } - -private[macroid] trait Resources { - import ResourceMacros._ - - case class Res(R: AnyRef) { - object string extends Dynamic { - def selectDynamic(id: String)(implicit ctx: AppContext): String = macro stringImpl - } - } -} - -object Resources extends Resources - -object ResourceMacros { - def stringImpl(c: MacroContext)(id: c.Expr[String])(ctx: c.Expr[AppContext]) = { - import c.universe._ - val Expr(Literal(Constant(idValue: String))) = id - val Select(Apply(_, List(r)), _) = c.prefix.tree - c.Expr[String](q"$ctx.get.getResources.getString($r.string.${newTermName(idValue)})") - } -} diff --git a/macroid-core/src/main/scala/macroid/ToastDsl.scala b/macroid-core/src/main/scala/macroid/ToastDsl.scala index aca7a2a..00bb6df 100644 --- a/macroid-core/src/main/scala/macroid/ToastDsl.scala +++ b/macroid-core/src/main/scala/macroid/ToastDsl.scala @@ -22,22 +22,22 @@ object Loafs extends Loafs private[macroid] trait ToastBuilding { /** Create a toast with the specified text */ - def toast(text: CharSequence)(implicit ctx: AppContext): Ui[Toast] = - Ui(Toast.makeText(ctx.get, text, Toast.LENGTH_SHORT)) + def toast(text: CharSequence)(implicit ctx: ContextWrapper): Ui[Toast] = + Ui(Toast.makeText(ctx.getOriginal, text, Toast.LENGTH_SHORT)) /** Create a toast with the specified resource ID */ - def toast(text: Int)(implicit ctx: AppContext): Ui[Toast] = - Ui(Toast.makeText(ctx.get, text, Toast.LENGTH_SHORT)) + def toast(text: Int)(implicit ctx: ContextWrapper): Ui[Toast] = + Ui(Toast.makeText(ctx.getOriginal, text, Toast.LENGTH_SHORT)) /** Create a toast with either the specified text or the specified resource ID */ - def toast(text: Either[Int, CharSequence])(implicit ctx: AppContext): Ui[Toast] = text match { - case Right(t) => Ui(Toast.makeText(ctx.get, t, Toast.LENGTH_SHORT)) - case Left(t) => Ui(Toast.makeText(ctx.get, t, Toast.LENGTH_SHORT)) + def toast(text: Either[Int, CharSequence])(implicit ctx: ContextWrapper): Ui[Toast] = text match { + case Right(t) ⇒ Ui(Toast.makeText(ctx.getOriginal, t, Toast.LENGTH_SHORT)) + case Left(t) ⇒ Ui(Toast.makeText(ctx.getOriginal, t, Toast.LENGTH_SHORT)) } /** Create a toast with the specified view */ - def toast(view: Ui[View])(implicit ctx: AppContext): Ui[Toast] = - view.map(v ⇒ new Toast(ctx.get) { setView(v); setDuration(Toast.LENGTH_SHORT) }) + def toast(view: Ui[View])(implicit ctx: ContextWrapper): Ui[Toast] = + view.map(v ⇒ new Toast(ctx.getOriginal) { setView(v); setDuration(Toast.LENGTH_SHORT) }) } object ToastBuilding extends ToastBuilding diff --git a/macroid-core/src/test/scala/macroid/TweakingSpec.scala b/macroid-core/src/test/scala/macroid/TweakingSpec.scala index 9adf237..6c2674c 100644 --- a/macroid-core/src/test/scala/macroid/TweakingSpec.scala +++ b/macroid-core/src/test/scala/macroid/TweakingSpec.scala @@ -2,13 +2,12 @@ package macroid import org.scalatest.FlatSpec import android.widget.{ LinearLayout, TextView, Button } -import android.app.Activity import LayoutDsl._ import Tweaks._ import contrib._ class TweakingSpec extends FlatSpec { - implicit val ctx = ActivityContext(null: Activity) + implicit val ctx: ContextWrapper = null "Tweaking" should "work with widgets and tweaks" in { def foo = { @@ -58,7 +57,7 @@ class TweakingSpec extends FlatSpec { } } - "TextTweaks.allCaps" should "be invokeable without parenthesis" in { + "TextTweaks.allCaps" should "be invokeable without parentheses" in { def foo = { w[TextView] <~ TextTweaks.allCaps } diff --git a/macroid-core/src/test/scala/macroid/UiSpec.scala b/macroid-core/src/test/scala/macroid/UiSpec.scala index 758e0a6..6aae031 100644 --- a/macroid-core/src/test/scala/macroid/UiSpec.scala +++ b/macroid-core/src/test/scala/macroid/UiSpec.scala @@ -1,14 +1,13 @@ package macroid import org.scalatest.FlatSpec -import android.app.Activity import android.widget.TextView import LayoutDsl._ import Tweaks._ import Snails._ class UiSpec extends FlatSpec { - implicit val ctx = ActivityContext(null: Activity) + implicit val ctx: ContextWrapper = null "UI actions" should "be composable" in { def foo = { diff --git a/macroid-docs/differences/Scaloid.md b/macroid-docs/differences/Scaloid.md index d1d3265..dbff0a1 100644 --- a/macroid-docs/differences/Scaloid.md +++ b/macroid-docs/differences/Scaloid.md @@ -53,7 +53,7 @@ so you can combine them and store in variables, traits or objects in any way you say ```scala -def largeText(str: String)(implicit appCtx: AppContext) = +def largeText(str: String)(implicit ctx: ContextWrapper) = text(str) + TextSize.large ``` @@ -92,14 +92,12 @@ The media queries are an integrated part of the UI language. For example, `hdpi just returns an `Option[Tweak[TextView]]`, so you can store and use it anywhere. This contrasts with *Scaloid*, where `if` conditionals are used. -## Safer implicit contexts +## Safer implicit context *Scaloid* always holds on to the Activity instance to store its `implicit` Context, which [may result in memory leaks](http://android-developers.blogspot.co.at/2009/01/avoiding-memory-leaks.html). -*Macroid* distinguishes between `AppContext` and `ActivityContext`, which are obtained and passed separately. -The former is stored as-is, since it is safe, while the latter is stored as a `WeakReference`, avoiding the -above problem. +*Macroid* uses `WeakReference`s to store `Context`s other than `Application`, avoiding the above problem. ## Safer threading diff --git a/macroid-docs/differences/Vanilla.md b/macroid-docs/differences/Vanilla.md index 8f6ef73..5859675 100644 --- a/macroid-docs/differences/Vanilla.md +++ b/macroid-docs/differences/Vanilla.md @@ -62,12 +62,10 @@ val futureText = Future { myTextView <~ futureText ``` -## Safer implicit contexts +## Safer implicit context -*Macroid* distinguishes between `AppContext` and `ActivityContext`, which are obtained and passed separately. -The former is stored as-is, since it is safe, while the latter is stored as a `WeakReference`. This helps avoid some -well-known problems: [memory leaks](http://stackoverflow.com/questions/3346080/android-references-to-a-context-and-memory-leaks), -[uncertainty about which one to use](http://stackoverflow.com/questions/987072/using-application-context-everywhere?rq=1). +*Macroid* uses `WeakReference`s to store `Context`s other than `Application`. This helps to automatically avoid +[memory leaks](http://stackoverflow.com/questions/3346080/android-references-to-a-context-and-memory-leaks). ## Safer threading diff --git a/macroid-docs/guide/Contexts.md b/macroid-docs/guide/Contexts.md index 771adc7..cf5b6f6 100644 --- a/macroid-docs/guide/Contexts.md +++ b/macroid-docs/guide/Contexts.md @@ -6,16 +6,21 @@ two types of context: one obtained from an activity and one obtained from the application. The reason is that holding on to the activity context may cause [memory leaks](http://stackoverflow.com/questions/3346080/android-references-to-a-context-and-memory-leaks). -*Macroid* distinguishes between these two types of Context and passes them implicitly to prevent code bloat. +*Macroid* automatically stores `Activity` and `Service` contexts as weak references, avoiding the problem. +This is done with the `ContextWrapper` trait, which has four methods: -## What are they +* `contextWrapper.application` will return the application context; +* `contextWrapper.original` will return a weak reference to the original context passed to `ContextWrapper`; +* `contextWrapper.getOriginal` is the same as above, but it will force the weak reference; +* `contextWrapper.bestAvailable` will return the original context if it’s available, + otherwise — the application context. -* `macroid.AppContext` holds the application context; -* `macroid.ActivityContext` holds a weak reference to the activity context, so that it’s safe to store it. +There are a few specialized cases of `ContextWrapper`: `ActivityContextWrapper`, `ServiceContextWrapper` +and `ApplicationContextWrapper`. ## Including -To include the implicit contexts in your activity, inherit `Contexts`: +To include the implicit context in your activity, inherit `Contexts`: ```scala import macroid.Contexts @@ -42,26 +47,41 @@ class MyFragment extends Fragment with Contexts[Fragment] { } ``` +You can also construct a `ContextWrapper` directly: + +```scala +val ctx = ContextWrapper(myActivity) +``` + ## Usage -Most *Macroid* APIs require one or two of these contexts. If you use them inside an -activity or a fragment, you are golden. However sometimes you need to pass the contexts +Most *Macroid* APIs require an implicit `ContextWrapper`. If you use them inside an +activity or a fragment, you are golden. However sometimes you need to pass the context to other methods: ```scala -import macroid.{ AppContext, ActivityContext } +import macroid.ContextWrapper import macroid.FullDsl._ import macroid.contrib._ -def customTweak(implicit appCtx: AppContext) = - // this one requires AppContext +def customTweak(implicit ctx: ContextWrapper) = TextTweaks.large + text("foo") -def customLayout(implicit ctx: ActivityContext) = - // layout bricks require ActivityContext +def customLayout(implicit ctx: ContextWrapper) = l[LinearLayout]( w[TextView], w[Button] ) ``` + +You can also require a specific type of context: + +```scala +import macroid.ActivityContextWrapper + +def needActivity(implicit ctx: ActivityContextWrapper) = + ctx.original.get foreach { activity: Activity ⇒ + ... + } +``` diff --git a/macroid-docs/guide/Imports.md b/macroid-docs/guide/Imports.md index b12d964..105720f 100644 --- a/macroid-docs/guide/Imports.md +++ b/macroid-docs/guide/Imports.md @@ -28,5 +28,5 @@ There are also some things ouside these modules: * [`macroid.Ui`](UiActions.html), [`macroid.Tweak`](Tweaks.html), [`macroid.Snail`](Snails.html), [`macroid.Transformer`](Transformers.html) -* [`macroid.{ AppContext, ActivityContext, Contexts }`](Contexts.html) +* [`macroid.{ ContextWrapper, Contexts }`](Contexts.html) * [`macroid.IdGeneration`](Searching.html#id-and-tag-generation) diff --git a/macroid-docs/guide/MediaQueries.md b/macroid-docs/guide/MediaQueries.md index 206fc3f..fecb585 100644 --- a/macroid-docs/guide/MediaQueries.md +++ b/macroid-docs/guide/MediaQueries.md @@ -39,7 +39,7 @@ button <~ padding(top = 3 dp) As you may have figured out, media queries can be used to define adaptive tweaks: ```scala -def orient(implicit appCtx: AppContext) = +def orient(implicit ctx: ContextWrapper) = landscape ? horizontal | vertical ``` @@ -53,7 +53,7 @@ onlySeenInHD <~ (hdpi ? show) On the other hand, several queries can be chained: ```scala -def adaptiveCaption(implicit appCtx: AppContext) = +def adaptiveCaption(implicit ctx: ContextWrapper) = widerThan(200 dp) ? text("fooobaaar") | widerThan(100 dp) ? text("foobar") | text("foo") diff --git a/macroid-docs/modules/Viewable.md b/macroid-docs/modules/Viewable.md index 453fac2..33ebcf7 100644 --- a/macroid-docs/modules/Viewable.md +++ b/macroid-docs/modules/Viewable.md @@ -23,7 +23,7 @@ import macroid.viewable.Viewable case class User(name: String) -def userViewable(implicit ctx: ActivityContext, appCtx: AppContext): Viewable[User, TextView] = +def userViewable(implicit ctx: ContextWrapper): Viewable[User, TextView] = Viewable[User] { user ⇒ w[TextView] <~ TextTweaks.large <~ text(user.name) } @@ -93,7 +93,7 @@ import macroid.viewable.Listable case class User(name: String) -def userListable(implicit ctx: ActivityContext, appCtx: AppContext): Listable[User, TextView] = +def userListable(implicit ctx: ContextWrapper): Listable[User, TextView] = Listable[User] { // create the layout w[TextView] <~ TextTweaks.large @@ -169,7 +169,7 @@ The “fill view” step in *listables* can be defined with a tweak: ```scala case class User(name: String, age: Int) -def userListable(implicit ctx: ActivityContext, appCtx: AppContext) = +def userListable(implicit ctx: ContextWrapper) = Listable[User].tw { // create layout w[TextView] @@ -185,7 +185,7 @@ Similarly, the same “fill view” step in *listables* can be defined with a tr ```scala case class User(name: String, picture: Bitmap) -def userListable(implicit ctx: ActivityContext, appCtx: AppContext) = +def userListable(implicit ctx: ContextWrapper) = Listable[User].tr { l[HorizontalLinearLayout]( w[ImageView], @@ -203,7 +203,7 @@ It is often required to have the *listable* enclosed into some container, for ex Here’s how this can be done: ```scala -def cardListable[A, W <: View](listable: Listable[A, W])(implicit ctx: ActivityContext, appCtx: AppContext) = +def cardListable[A, W <: View](listable: Listable[A, W])(implicit ctx: ContextWrapper) = Listable.wrap(listable) { w ⇒ l[CardView](w) <~ Styles.card } @@ -221,7 +221,7 @@ and we have already defined *listables* for `Long` and `Bitmap`. To combine them ```scala // this will be a Listable[(Long, Bitmap), View] -def combinedListable(implicit ctx: ActivityContext, appCtx: AppContext) = +def combinedListable(implicit ctx: ContextWrapper) = Listable.combine(timestampListable, pictureListable) { (t, p) ⇒ l[VerticalLinearLayout](t, p) } @@ -253,7 +253,7 @@ object UserListable extends SlottedListable[User] { // now we just need to implement two abstract methods of SlottedListable: // make and wire the slots - def makeSlots(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = { + def makeSlots(viewType: Int)(implicit ctx: ContextWrapper) = { val slots = new Slots val view = l[LinearLayout]( w[ImageView] <~ wire(slots.picture), @@ -263,7 +263,7 @@ object UserListable extends SlottedListable[User] { } // fill the slots - def fillSlots(slots: Slots, data: User)(implicit ctx: ActivityContext, appCtx: AppContext) = { + def fillSlots(slots: Slots, data: User)(implicit ctx: ContextWrapper) = { (slots.name <~ text(data.name)) ~ (slots.picture <~ data.picture) } diff --git a/macroid-docs/tutorial/BuildingLayouts.md b/macroid-docs/tutorial/BuildingLayouts.md index e8b8848..7de653e 100644 --- a/macroid-docs/tutorial/BuildingLayouts.md +++ b/macroid-docs/tutorial/BuildingLayouts.md @@ -46,24 +46,24 @@ Unlike Android XML, we can declare the widgets wherever we want and stack them together as needed: ```scala -// ActivityContext is an Android Context obtained from an Activity -import macroid.ActivityContext +// ContextWrapper is a safer Context wrapper +import macroid.ContextWrapper // A module with layouts object OurLayouts { - def layout1(implicit ctx: ActivityContext) = + def layout1(implicit ctx: ContextWrapper) = l[LinearLayout]( w[TextView], w[ImageView], w[Button] ) - def layout2(implicit ctx: ActivityContext) = + def layout2(implicit ctx: ContextWrapper) = l[FrameLayout]( w[ProgressBar] ) - def layout3(implicit ctx: ActivityContext) = + def layout3(implicit ctx: ContextWrapper) = l[FrameLayout]( layout1, layout2 diff --git a/macroid-docs/tutorial/CompleteCode.md b/macroid-docs/tutorial/CompleteCode.md index 7c3ade5..012fa45 100644 --- a/macroid-docs/tutorial/CompleteCode.md +++ b/macroid-docs/tutorial/CompleteCode.md @@ -11,12 +11,12 @@ import macroid.contrib._ import macroid.FullDsl._ object OurTweaks { - def greeting(greeting: String)(implicit appCtx: AppContext) = + def greeting(greeting: String)(implicit ctx: ContextWrapper) = TextTweaks.large + text(greeting) + hide - def orient(implicit appCtx: AppContext) = + def orient(implicit ctx: ContextWrapper) = landscape ? horizontal | vertical } diff --git a/macroid-docs/tutorial/GettingAdaptive.md b/macroid-docs/tutorial/GettingAdaptive.md index 24b0ccf..40807f1 100644 --- a/macroid-docs/tutorial/GettingAdaptive.md +++ b/macroid-docs/tutorial/GettingAdaptive.md @@ -17,7 +17,7 @@ We are going to incorporate this into our layout more elegantly by defining a he object OurTweaks { ... // the new helper - def orient(implicit appCtx: AppContext) = + def orient(implicit ctx: ContextWrapper) = landscape ? horizontal | vertical } ... diff --git a/macroid-docs/tutorial/Tweaking.md b/macroid-docs/tutorial/Tweaking.md index e7bfbd1..6e2f5ba 100644 --- a/macroid-docs/tutorial/Tweaking.md +++ b/macroid-docs/tutorial/Tweaking.md @@ -26,14 +26,14 @@ l[LinearLayout]( Of course, we can do much fancier than that! Remember the composability principle? Tweaks are composable as well: ```scala -// AppContext is an Android Context obtained from getApplicationContext -import macroid.AppContext +// ContextWrapper is a safer Context wrapper +import macroid.ContextWrapper // More tweaks import macroid.contrib.TextTweaks // A module with custom tweaks object OurTweaks { - def greeting(greeting: String)(implicit appCtx: AppContext) = + def greeting(greeting: String)(implicit ctx: ContextWrapper) = TextTweaks.large + text(greeting) + hide diff --git a/macroid-viewable/src/main/scala/macroid/viewable/Listable.scala b/macroid-viewable/src/main/scala/macroid/viewable/Listable.scala index e3cc819..0741b9a 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/Listable.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/Listable.scala @@ -23,17 +23,17 @@ trait PartialListable[A, +W <: View] { self ⇒ def viewType(data: A): Option[Int] /** Create the layout */ - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext): Ui[W] + def makeView(viewType: Int)(implicit ctx: ContextWrapper): Ui[W] /** Fill the layout with data. Returns None if not defined for this value */ - def fillView[W1 >: W <: View](view: Ui[W1], data: A)(implicit ctx: ActivityContext, appCtx: AppContext): Option[Ui[W1]] + def fillView[W1 >: W <: View](view: Ui[W1], data: A)(implicit ctx: ContextWrapper): Option[Ui[W1]] /** Map the underlying data type `A` */ def contraFlatMap[B](f: B ⇒ Option[A]): PartialListable[B, W] = new PartialListable[B, W] { def viewTypeCount = self.viewTypeCount def viewType(data: B) = f(data).flatMap(self.viewType) - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView[W1 >: W <: View](view: Ui[W1], data: B)(implicit ctx: ActivityContext, appCtx: AppContext) = f(data).flatMap(self.fillView(view, _)) + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView[W1 >: W <: View](view: Ui[W1], data: B)(implicit ctx: ContextWrapper) = f(data).flatMap(self.fillView(view, _)) } /** Combine with an alternative partial */ @@ -47,14 +47,14 @@ trait PartialListable[A, +W <: View] { self ⇒ self.viewType(data) orElse alternative.viewType(data).map(_ + self.viewTypeCount) - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = if (viewType < self.viewTypeCount) { self.makeView(viewType) } else { alternative.makeView(viewType - self.viewTypeCount) } - def fillView[W2 >: W1 <: View](view: Ui[W2], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def fillView[W2 >: W1 <: View](view: Ui[W2], data: A)(implicit ctx: ContextWrapper) = self.fillView(view, data) orElse alternative.fillView(view, data) } @@ -65,8 +65,8 @@ trait PartialListable[A, +W <: View] { self ⇒ def viewTypeCount = self.viewTypeCount def viewType(data: A) = if (p(data)) self.viewType(data) else None - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView[W1 >: W <: View](view: Ui[W1], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView[W1 >: W <: View](view: Ui[W1], data: A)(implicit ctx: ContextWrapper) = if (p(data)) self.fillView(view, data) else None } @@ -78,8 +78,8 @@ trait PartialListable[A, +W <: View] { self ⇒ case x: A ⇒ self.viewType(x) case _ ⇒ None } - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView[W1 >: W <: View](view: Ui[W1], data: B)(implicit ctx: ActivityContext, appCtx: AppContext) = data match { + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView[W1 >: W <: View](view: Ui[W1], data: B)(implicit ctx: ContextWrapper) = data match { case x: A ⇒ self.fillView(view, x) case _ ⇒ None } @@ -90,8 +90,8 @@ trait PartialListable[A, +W <: View] { self ⇒ new Listable[A, W1] { def viewTypeCount = self.viewTypeCount def viewType(data: A) = self.viewType(data).get - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView(view: Ui[W1], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = self.fillView(view, data).get + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView(view: Ui[W1], data: A)(implicit ctx: ContextWrapper) = self.fillView(view, data).get } } @@ -110,18 +110,18 @@ trait Listable[A, W <: View] { self ⇒ def viewType(data: A): Int /** Create the layout */ - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext): Ui[W] + def makeView(viewType: Int)(implicit ctx: ContextWrapper): Ui[W] /** Fill the layout with data. Will be always called with layouts created by `makeView` using `viewType(data)` */ - def fillView(view: Ui[W], data: A)(implicit ctx: ActivityContext, appCtx: AppContext): Ui[W] + def fillView(view: Ui[W], data: A)(implicit ctx: ContextWrapper): Ui[W] /** Map the underlying data type `A` */ def contraMap[B](f: B ⇒ A): Listable[B, W] = new Listable[B, W] { def viewTypeCount = self.viewTypeCount def viewType(data: B) = self.viewType(f(data)) - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView(view: Ui[W], data: B)(implicit ctx: ActivityContext, appCtx: AppContext) = self.fillView(view, f(data)) + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView(view: Ui[W], data: B)(implicit ctx: ContextWrapper) = self.fillView(view, f(data)) } /** Add extra fillView function */ @@ -129,8 +129,8 @@ trait Listable[A, W <: View] { self ⇒ new Listable[A, W] { def viewTypeCount = self.viewTypeCount def viewType(data: A) = self.viewType(data) - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView(view: Ui[W], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView(view: Ui[W], data: A)(implicit ctx: ContextWrapper) = fill(self.fillView(view, data), data) } @@ -139,8 +139,8 @@ trait Listable[A, W <: View] { self ⇒ new PartialListable[A, W] { def viewTypeCount = self.viewTypeCount def viewType(data: A) = Some(self.viewType(data)) - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = self.makeView(viewType) - def fillView[W1 >: W <: View](view: Ui[W1], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = view match { + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = self.makeView(viewType) + def fillView[W1 >: W <: View](view: Ui[W1], data: A)(implicit ctx: ContextWrapper) = view match { case x: Ui[W] ⇒ Some(self.fillView(x, data)) case _ ⇒ None } @@ -155,16 +155,16 @@ trait Listable[A, W <: View] { self ⇒ /** Convert to a viewable */ def toViewable: Viewable[A, W] = new Viewable[A, W] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: A)(implicit ctx: ContextWrapper) = fillView(makeView(viewType(data)), data) } /** An adapter to use with a `ListView` */ - def listAdapter(data: Seq[A])(implicit ctx: ActivityContext, appCtx: AppContext): ListableListAdapter[A, W] = - new ListableListAdapter[A, W](data)(ctx, appCtx, this) + def listAdapter(data: Seq[A])(implicit ctx: ContextWrapper): ListableListAdapter[A, W] = + new ListableListAdapter[A, W](data)(ctx, this) /** A tweak to set the adapter of a `ListView` */ - def listAdapterTweak(data: Seq[A])(implicit ctx: ActivityContext, appCtx: AppContext) = + def listAdapterTweak(data: Seq[A])(implicit ctx: ContextWrapper) = ListTweaks.adapter(listAdapter(data)) } @@ -175,8 +175,8 @@ class ListableBuilder[A] { new Listable[A, W] { val viewTypeCount = 1 def viewType(data: A) = 0 - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = make - def fillView(view: Ui[W], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = fill(view)(data) + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = make + def fillView(view: Ui[W], data: A)(implicit ctx: ContextWrapper) = fill(view)(data) } /** Define a listable by providing the make function and a tweak to fill the layout with data */ @@ -184,8 +184,8 @@ class ListableBuilder[A] { new Listable[A, W] { val viewTypeCount = 1 def viewType(data: A) = 0 - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = make - def fillView(view: Ui[W], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = view <~ fill(data) + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = make + def fillView(view: Ui[W], data: A)(implicit ctx: ContextWrapper) = view <~ fill(data) } /** Define a listable by providing the make function and a transformer to fill the layout with data */ @@ -193,8 +193,8 @@ class ListableBuilder[A] { new Listable[A, W] { val viewTypeCount = 1 def viewType(data: A) = 0 - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = make - def fillView(view: Ui[W], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = view <~ fill(data) + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = make + def fillView(view: Ui[W], data: A)(implicit ctx: ContextWrapper) = view <~ fill(data) } } @@ -207,8 +207,8 @@ object Listable { new Listable[String, TextView] { val viewTypeCount = 1 def viewType(data: String) = 0 - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = w[TextView] <~ tweak - def fillView(view: Ui[TextView], data: String)(implicit ctx: ActivityContext, appCtx: AppContext) = view <~ Tweaks.text(data) + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = w[TextView] <~ tweak + def fillView(view: Ui[TextView], data: String)(implicit ctx: ContextWrapper) = view <~ Tweaks.text(data) } /** An alias for SlottedListable */ @@ -224,13 +224,13 @@ object Listable { override val viewTypeCount = x.viewTypeCount override def viewType(data: A) = x.viewType(data) - def makeSlots(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = { + def makeSlots(viewType: Int)(implicit ctx: ContextWrapper) = { val slots = new Slots val view = wrapper(x.makeView(viewType) <~ wire(slots.x)) (view, slots) } - def fillSlots(slots: Slots, data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def fillSlots(slots: Slots, data: A)(implicit ctx: ContextWrapper) = slots.x.fold(Ui.nop)(z ⇒ x.fillView(Ui(z), data) ~ Ui.nop) } @@ -250,7 +250,7 @@ object Listable { override def viewType(data: (A1, A2)) = x.viewType(data._1) * y.viewTypeCount + y.viewType(data._2) - def makeSlots(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = { + def makeSlots(viewType: Int)(implicit ctx: ContextWrapper) = { val slots = new Slots val (xType, yType) = (viewType / y.viewTypeCount, viewType % y.viewTypeCount) val view = glue( @@ -260,7 +260,7 @@ object Listable { (view, slots) } - def fillSlots(slots: Slots, data: (A1, A2))(implicit ctx: ActivityContext, appCtx: AppContext) = + def fillSlots(slots: Slots, data: (A1, A2))(implicit ctx: ContextWrapper) = slots.x.fold(Ui.nop)(z ⇒ x.fillView(Ui(z), data._1) ~ Ui.nop) ~ slots.y.fold(Ui.nop)(z ⇒ y.fillView(Ui(z), data._2) ~ Ui.nop) } diff --git a/macroid-viewable/src/main/scala/macroid/viewable/ListableListAdapter.scala b/macroid-viewable/src/main/scala/macroid/viewable/ListableListAdapter.scala index a215401..09bad3d 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/ListableListAdapter.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/ListableListAdapter.scala @@ -4,11 +4,11 @@ import android.view.{ View, ViewGroup } import android.widget.ArrayAdapter import macroid.UiThreading._ import macroid.util.SafeCast -import macroid.{ ActivityContext, AppContext, Ui } +import macroid.{ContextWrapper, Ui} /** A `ListAdapter` based on the `Listable` typeclass */ -class ListableListAdapter[A, W <: View](data: Seq[A])(implicit ctx: ActivityContext, appCtx: AppContext, listable: Listable[A, W]) - extends ArrayAdapter[A](ctx.get, 0) { +class ListableListAdapter[A, W <: View](data: Seq[A])(implicit ctx: ContextWrapper, listable: Listable[A, W]) + extends ArrayAdapter[A](ctx.getOriginal, 0) { addAll(data: _*) diff --git a/macroid-viewable/src/main/scala/macroid/viewable/SlottedListable.scala b/macroid-viewable/src/main/scala/macroid/viewable/SlottedListable.scala index 8c09fe7..554f719 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/SlottedListable.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/SlottedListable.scala @@ -4,7 +4,7 @@ import android.view.View import macroid.LayoutDsl._ import macroid.Tweaks._ import macroid.util.SafeCast -import macroid.{ Ui, AppContext, ActivityContext } +import macroid.{ContextWrapper, Ui} /** A `Listable` that works by saving widget slots inside the layout's tag and filling them later */ trait SlottedListable[A] extends Listable[A, View] { @@ -27,21 +27,21 @@ trait SlottedListable[A] extends Listable[A, View] { * (view, slots) * }}} */ - def makeSlots(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext): (Ui[View], Slots) + def makeSlots(viewType: Int)(implicit ctx: ContextWrapper): (Ui[View], Slots) /** Fill the slots with data */ - def fillSlots(slots: Slots, data: A)(implicit ctx: ActivityContext, appCtx: AppContext): Ui[Any] + def fillSlots(slots: Slots, data: A)(implicit ctx: ContextWrapper): Ui[Any] // Implemented for convenience, override if necessary def viewTypeCount = 1 def viewType(data: A) = 0 - def makeView(viewType: Int)(implicit ctx: ActivityContext, appCtx: AppContext) = { + def makeView(viewType: Int)(implicit ctx: ContextWrapper) = { val (v, s) = makeSlots(viewType) v <~ hold(s) } - def fillView(view: Ui[View], data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = view flatMap { v ⇒ + def fillView(view: Ui[View], data: A)(implicit ctx: ContextWrapper) = view flatMap { v ⇒ val (v1, s) = SafeCast[Any, Slots](v.getTag).map(x ⇒ (Ui(v), x)).getOrElse(makeSlots(viewType(data))) fillSlots(s, data).flatMap(_ ⇒ v1) } diff --git a/macroid-viewable/src/main/scala/macroid/viewable/Viewable.scala b/macroid-viewable/src/main/scala/macroid/viewable/Viewable.scala index d988535..e07a61e 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/Viewable.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/Viewable.scala @@ -13,33 +13,33 @@ import scala.reflect.ClassTag /** Expresses the fact that *some* of the values of type `A` can be displayed with a widget or layout of type `W` */ trait PartialViewable[A, +W <: View] { self ⇒ /** Create the layout for a value of type `A`. Returns None if not defined for this value */ - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext): Option[Ui[W]] + def view(data: A)(implicit ctx: ContextWrapper): Option[Ui[W]] /** Map the underlying data type `A` */ def contraFlatMap[B](f: B ⇒ Option[A]): PartialViewable[B, W] = new PartialViewable[B, W] { - def view(data: B)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: B)(implicit ctx: ContextWrapper) = f(data).flatMap(self.view) } /** Combine with an alternative partial */ def orElse[W1 >: W <: View](alternative: PartialViewable[A, W1]): PartialViewable[A, W1] = new PartialViewable[A, W1] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: A)(implicit ctx: ContextWrapper) = self.view(data) orElse alternative.view(data) } /** Specify a condition to further limit this partial */ def cond(p: A ⇒ Boolean): PartialViewable[A, W] = new PartialViewable[A, W] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: A)(implicit ctx: ContextWrapper) = if (p(data)) self.view(data) else None } /** Make a partial defined for a subset of the provided supertype */ def toParent[B](implicit classTag: ClassTag[A]): PartialViewable[B, W] = new PartialViewable[B, W] { - def view(data: B)(implicit ctx: ActivityContext, appCtx: AppContext) = data match { + def view(data: B)(implicit ctx: ContextWrapper) = data match { case x: A ⇒ self.view(x) case _ ⇒ None } @@ -48,7 +48,7 @@ trait PartialViewable[A, +W <: View] { self ⇒ /** Convert back to total viewable */ def toTotal: Viewable[A, W] = new Viewable[A, W] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: A)(implicit ctx: ContextWrapper) = self.view(data).get } } @@ -57,19 +57,19 @@ trait PartialViewable[A, +W <: View] { self ⇒ @implicitNotFound("Don't know how to display data type ${A}. Try importing an instance of Viewable[${A}, ...]") trait Viewable[A, +W <: View] { self ⇒ /** Create the layout for a value of type `A` */ - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext): Ui[W] + def view(data: A)(implicit ctx: ContextWrapper): Ui[W] /** Map the underlying data type `A` */ def contraMap[B](f: B ⇒ A): Viewable[B, W] = new Viewable[B, W] { - def view(data: B)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: B)(implicit ctx: ContextWrapper) = self.view(f(data)) } /** Convert to partial viewable for composition with alternatives */ def toPartial: PartialViewable[A, W] = new PartialViewable[A, W] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: A)(implicit ctx: ContextWrapper) = Some(self.view(data)) } @@ -80,11 +80,11 @@ trait Viewable[A, +W <: View] { self ⇒ def toParent[B](implicit evidence: ClassTag[A]): PartialViewable[B, W] = toPartial.toParent[B] /** An adapter to use with a `ViewPager` */ - def pagerAdapter(data: Seq[A])(implicit ctx: ActivityContext, appCtx: AppContext): ViewablePagerAdapter[A, W] = - new ViewablePagerAdapter[A, W](data)(ctx, appCtx, this) + def pagerAdapter(data: Seq[A])(implicit ctx: ContextWrapper): ViewablePagerAdapter[A, W] = + new ViewablePagerAdapter[A, W](data)(ctx, this) /** A tweak to set the adapter of a `ViewPager` */ - def pagerAdapterTweak(data: Seq[A])(implicit ctx: ActivityContext, appCtx: AppContext) = + def pagerAdapterTweak(data: Seq[A])(implicit ctx: ContextWrapper) = PagerTweaks.adapter(pagerAdapter(data)) } @@ -93,14 +93,14 @@ class ViewableBuilder[A] { /** Define a viewable by providing the layout function */ def apply[W <: View](layout: A ⇒ Ui[W]): Viewable[A, W] = new Viewable[A, W] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = layout(data) + def view(data: A)(implicit ctx: ContextWrapper) = layout(data) } } object Viewable { implicit def `Listable is Viewable`[A, W <: View](implicit listable: Listable[A, W]): Viewable[A, W] = new Viewable[A, W] { - def view(data: A)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: A)(implicit ctx: ContextWrapper) = listable.fillView(listable.makeView(listable.viewType(data)), data) } @@ -110,7 +110,7 @@ object Viewable { /** Define a viewable for strings by providing the `TextView` style */ def text(tweak: Tweak[TextView]): Viewable[String, TextView] = new Viewable[String, TextView] { - def view(data: String)(implicit ctx: ActivityContext, appCtx: AppContext) = + def view(data: String)(implicit ctx: ContextWrapper) = w[TextView] <~ tweak <~ Tweaks.text(data) } } diff --git a/macroid-viewable/src/main/scala/macroid/viewable/ViewablePagerAdapter.scala b/macroid-viewable/src/main/scala/macroid/viewable/ViewablePagerAdapter.scala index 41e7e83..cfb024d 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/ViewablePagerAdapter.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/ViewablePagerAdapter.scala @@ -2,11 +2,11 @@ package macroid.viewable import android.support.v4.view.PagerAdapter import android.view.{ ViewGroup, View } -import macroid.{ AppContext, ActivityContext } +import macroid.ContextWrapper import macroid.UiThreading._ /** A `PagerAdapter` based on the `Viewable` typeclass */ -class ViewablePagerAdapter[A, +W <: View](data: Seq[A])(implicit ctx: ActivityContext, appCtx: AppContext, viewable: Viewable[A, W]) extends PagerAdapter { +class ViewablePagerAdapter[A, +W <: View](data: Seq[A])(implicit ctx: ContextWrapper, viewable: Viewable[A, W]) extends PagerAdapter { override def instantiateItem(container: ViewGroup, position: Int) = { val view = getUi(viewable.view(data(position))) container.addView(view, 0) diff --git a/macroid-viewable/src/main/scala/macroid/viewable/package.scala b/macroid-viewable/src/main/scala/macroid/viewable/package.scala index d460292..5c7ffed 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/package.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/package.scala @@ -5,27 +5,27 @@ import android.view.View package object viewable { implicit class ViewableOps[A](data: A) { /** Create layout for this value */ - def view[W <: View](implicit ctx: ActivityContext, appCtx: AppContext, viewable: Viewable[A, W]) = + def view[W <: View](implicit ctx: ContextWrapper, viewable: Viewable[A, W]) = viewable.view(data) } implicit class PagerAdapterOps[A](data: Seq[A]) { /** Create a `PagerAdapter` for this sequence */ - def pagerAdapter[W <: View](implicit ctx: ActivityContext, appCtx: AppContext, viewable: Viewable[A, W]) = + def pagerAdapter[W <: View](implicit ctx: ContextWrapper, viewable: Viewable[A, W]) = viewable.pagerAdapter(data) /** Create a tweak to set the `PagerAdapter` with this sequence */ - def pagerAdapterTweak[W <: View](implicit ctx: ActivityContext, appCtx: AppContext, viewable: Viewable[A, W]) = + def pagerAdapterTweak[W <: View](implicit ctx: ContextWrapper, viewable: Viewable[A, W]) = viewable.pagerAdapterTweak(data) } implicit class ListAdapterOps[A](data: Seq[A]) { /** Create a `ListAdapter` for this sequence */ - def listAdapter[W <: View](implicit ctx: ActivityContext, appCtx: AppContext, listable: Listable[A, W]) = + def listAdapter[W <: View](implicit ctx: ContextWrapper, listable: Listable[A, W]) = listable.listAdapter(data) /** Create a tweak to set the `ListAdapter` with this sequence */ - def listAdapterTweak[W <: View](implicit ctx: ActivityContext, appCtx: AppContext, listable: Listable[A, W]) = + def listAdapterTweak[W <: View](implicit ctx: ContextWrapper, listable: Listable[A, W]) = listable.listAdapterTweak(data) } } diff --git a/project/plugins.sbt b/project/plugins.sbt index cae174f..30ad898 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.3.19") +addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.3.20") addSbtPlugin("me.lessis" % "bintray-sbt" % "0.2.1")