From eadae1e1c68978e5343090c52e5ed8ba69ab641e Mon Sep 17 00:00:00 2001 From: Nick Stanchenko Date: Sat, 11 Apr 2015 03:15:12 +0100 Subject: [PATCH] ! all: Replace AppContext and ActivityContext with ContextWrapper 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. --- build.sbt | 2 +- .../src/main/scala/macroid/Cakes.scala | 1 - .../src/main/scala/macroid/Contexts.scala | 54 ++++++++------ .../src/main/scala/macroid/DialogDsl.scala | 10 +-- .../main/scala/macroid/FragmentBuilding.scala | 16 ++--- .../main/scala/macroid/LayoutBuilding.scala | 18 ++--- .../src/main/scala/macroid/MediaQueries.scala | 46 ++++++------ .../src/main/scala/macroid/Resources.scala | 26 ------- .../src/main/scala/macroid/ToastDsl.scala | 8 +-- .../scala/macroid/contrib/ExtraTweaks.scala | 4 +- .../src/test/scala/macroid/TweakingSpec.scala | 6 +- .../src/test/scala/macroid/UiSpec.scala | 4 +- macroid-docs/differences/Scaloid.md | 8 +-- macroid-docs/differences/Vanilla.md | 8 +-- macroid-docs/guide/Contexts.md | 30 ++++---- macroid-docs/guide/Imports.md | 2 +- macroid-docs/guide/MediaQueries.md | 4 +- macroid-docs/modules/Viewable.md | 16 ++--- macroid-docs/tutorial/BuildingLayouts.md | 10 +-- macroid-docs/tutorial/CompleteCode.md | 4 +- macroid-docs/tutorial/GettingAdaptive.md | 2 +- macroid-docs/tutorial/Tweaking.md | 6 +- .../scala/macroid/viewable/Listable.scala | 72 +++++++++---------- .../viewable/ListableListAdapter.scala | 4 +- .../macroid/viewable/SlottedListable.scala | 10 +-- .../scala/macroid/viewable/Viewable.scala | 30 ++++---- .../viewable/ViewablePagerAdapter.scala | 4 +- .../main/scala/macroid/viewable/package.scala | 10 +-- project/plugins.sbt | 2 +- 29 files changed, 202 insertions(+), 215 deletions(-) delete mode 100644 macroid-core/src/main/scala/macroid/Resources.scala diff --git a/build.sbt b/build.sbt index f38058f..156f9ca 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ val commonSettings = androidBuildAar ++ bintrayPublishSettings ++ Seq( platformTarget in Android := "android-21", organization := "org.macroid", - version := "2.0.0-M4", + version := "2.0.0-SNAPSHOT", licenses += ("MIT", url("http://opensource.org/licenses/MIT")), scalaVersion := "2.10.5", diff --git a/macroid-core/src/main/scala/macroid/Cakes.scala b/macroid-core/src/main/scala/macroid/Cakes.scala index 4bd5aa0..f2737aa 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 Logging with MediaQueries diff --git a/macroid-core/src/main/scala/macroid/Contexts.scala b/macroid-core/src/main/scala/macroid/Contexts.scala index 5c8e41e..d92f273 100644 --- a/macroid-core/src/main/scala/macroid/Contexts.scala +++ b/macroid-core/src/main/scala/macroid/Contexts.scala @@ -1,41 +1,55 @@ 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) + */ +case class ContextWrapper(originalContext: Option[WeakReference[Context]], appContext: Context) { + def get = originalContext.flatMap(_.get) getOrElse appContext } -@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() -} -object ActivityContext { - def apply(activity: Activity) = new ActivityContext(WeakReference(activity)) +object ContextWrapper { + def apply(activity: Activity): ContextWrapper = + ContextWrapper(Some(WeakReference(activity)), activity.getApplicationContext) + + def apply(app: Application): ContextWrapper = + ContextWrapper(None, app) + + def apply(service: Service): ContextWrapper = + ContextWrapper(Some(WeakReference(service)), service.getApplicationContext) + + def apply[F](fragment: F)(implicit fragmentImpl: Fragment[F]): ContextWrapper = + 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) = + ContextWrapper(activity(self)) - implicit def fragmentAppContext(implicit fragment: Fragment[X]) = - AppContext(fragment.activity(self).getApplicationContext) + implicit def fragmentContextWrapper(implicit fragment: Fragment[X]) = + ContextWrapper(self)(fragment) - implicit def fragmentActivityContext(implicit fragment: Fragment[X]) = - ActivityContext(fragment.activity(self)) + implicit def serviceContextWrapper(implicit service: X <:< Service) = + 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..fa9d2d7 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) = + private def builder(implicit ctx: ContextWrapper) = theme.fold(new AlertDialog.Builder(ctx.get))(t ⇒ new AlertDialog.Builder(ctx.get, 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..accff8b 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._ @@ -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..613b99e 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,17 +33,17 @@ 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))") } - 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))") } - 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) })") diff --git a/macroid-core/src/main/scala/macroid/MediaQueries.scala b/macroid-core/src/main/scala/macroid/MediaQueries.scala index bb0b716..49667fe 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.appContext.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.appContext.getResources.getConfiguration.orientation == Configuration.ORIENTATION_PORTRAIT) + def landscape(implicit ctx: ContextWrapper) = + MediaQuery(ctx.appContext.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..0059dd4 100644 --- a/macroid-core/src/main/scala/macroid/ToastDsl.scala +++ b/macroid-core/src/main/scala/macroid/ToastDsl.scala @@ -22,21 +22,21 @@ object Loafs extends Loafs private[macroid] trait ToastBuilding { /** Create a toast with the specified text */ - def toast(text: CharSequence)(implicit ctx: AppContext): Ui[Toast] = + def toast(text: CharSequence)(implicit ctx: ContextWrapper): Ui[Toast] = Ui(Toast.makeText(ctx.get, text, Toast.LENGTH_SHORT)) /** Create a toast with the specified resource ID */ - def toast(text: Int)(implicit ctx: AppContext): Ui[Toast] = + def toast(text: Int)(implicit ctx: ContextWrapper): Ui[Toast] = Ui(Toast.makeText(ctx.get, 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 { + def toast(text: Either[Int, CharSequence])(implicit ctx: ContextWrapper): 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)) } /** Create a toast with the specified view */ - def toast(view: Ui[View])(implicit ctx: AppContext): Ui[Toast] = + def toast(view: Ui[View])(implicit ctx: ContextWrapper): Ui[Toast] = view.map(v ⇒ new Toast(ctx.get) { setView(v); setDuration(Toast.LENGTH_SHORT) }) } diff --git a/macroid-core/src/main/scala/macroid/contrib/ExtraTweaks.scala b/macroid-core/src/main/scala/macroid/contrib/ExtraTweaks.scala index 93b3146..5e778d7 100644 --- a/macroid-core/src/main/scala/macroid/contrib/ExtraTweaks.scala +++ b/macroid-core/src/main/scala/macroid/contrib/ExtraTweaks.scala @@ -27,9 +27,9 @@ object TextTweaks { /** Set a typeface with the given name * * Example: - * {{ + * {{{ * w[TextView] <~ typeface("sans-serif-light") - * }} + * }}} */ def typeface(name: String) = Tweak[W](x ⇒ x.setTypeface(Typeface.create(name, 0), typefaceStyle(x))) diff --git a/macroid-core/src/test/scala/macroid/TweakingSpec.scala b/macroid-core/src/test/scala/macroid/TweakingSpec.scala index 9adf237..71fdf74 100644 --- a/macroid-core/src/test/scala/macroid/TweakingSpec.scala +++ b/macroid-core/src/test/scala/macroid/TweakingSpec.scala @@ -1,14 +1,14 @@ package macroid +import android.content.Context 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(None, null: Context) "Tweaking" should "work with widgets and tweaks" in { def foo = { @@ -58,7 +58,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..562ce9f 100644 --- a/macroid-core/src/test/scala/macroid/UiSpec.scala +++ b/macroid-core/src/test/scala/macroid/UiSpec.scala @@ -1,14 +1,14 @@ package macroid +import android.content.Context 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(None, null: Context) "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..bc44845 100644 --- a/macroid-docs/guide/Contexts.md +++ b/macroid-docs/guide/Contexts.md @@ -6,16 +6,16 @@ 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` class, which has two methods: -## What are they - -* `macroid.AppContext` holds the application context; -* `macroid.ActivityContext` holds a weak reference to the activity context, so that it’s safe to store it. +* `contextWrapper.appContext` will always return the application context; +* `contextWrapper.get` will return the original context passed to `ContextWrapper`, + unless it was not garbage collected, in which case it defaults to `appContext`. ## 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,24 +42,28 @@ 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] diff --git a/macroid-docs/guide/Imports.md b/macroid-docs/guide/Imports.md index e264f19..b68b494 100644 --- a/macroid-docs/guide/Imports.md +++ b/macroid-docs/guide/Imports.md @@ -29,6 +29,6 @@ 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) * [`macroid.AutoLogTag`](Logging.html) \ No newline at end of file diff --git a/macroid-docs/guide/MediaQueries.md b/macroid-docs/guide/MediaQueries.md index dd0a098..1fd6458 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..16ed9a4 100644 --- a/macroid-viewable/src/main/scala/macroid/viewable/ListableListAdapter.scala +++ b/macroid-viewable/src/main/scala/macroid/viewable/ListableListAdapter.scala @@ -4,10 +4,10 @@ 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]) +class ListableListAdapter[A, W <: View](data: Seq[A])(implicit ctx: ContextWrapper, listable: Listable[A, W]) extends ArrayAdapter[A](ctx.get, 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")