Skip to content

Commit

Permalink
Add neko.listeners.adapter-view namespace + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sattvik committed Mar 13, 2011
1 parent a18d51b commit f177fdf
Show file tree
Hide file tree
Showing 2 changed files with 254 additions and 0 deletions.
130 changes: 130 additions & 0 deletions src/clojure/neko/listeners/adapter_view.clj
@@ -0,0 +1,130 @@
; Copyright © 2011 Sattvik Software & Technology Resources, Ltd. Co.
; All rights reserved.
;
; This program and the accompanying materials are made available under the
; terms of the Eclipse Public License v1.0 which accompanies this distribution,
; and is available at <http://www.eclipse.org/legal/epl-v10.html>.
;
; By using this software in any fashion, you are agreeing to be bound by the
; terms of this license. You must not remove this notice, or any other, from
; this software.

(ns neko.listeners.adapter-view
"Uility functions and macros for creating listeners corresponding to the
android.widget.AdapterView class."
{:author "Daniel Solano Gómez"})

(defn on-item-click-call
"Takes a function and yields an AdapterView.OnItemClickListener object that
will invoke the function. This function must take the following four
arguments:
parent the AdapterView where the click happened
view the view within the AdapterView that was clicked
position the position of the view in the adapter
id the row id of the item that was clicked"
[handler-fn]
{:pre [(fn? handler-fn)]
:post [(instance? android.widget.AdapterView$OnItemClickListener %)]}
(reify android.widget.AdapterView$OnItemClickListener
(onItemClick [this parent view position id]
(handler-fn parent view position id))))

(defmacro on-item-click
"Takes a body of expressions and yields an AdapterView.OnItemClickListener
object that will invoke the body. The body takes the following implicit
arguments:
parent the AdapterView where the click happened
view the view within the AdapterView that was clicked
position the position of the view in the adapter
id the row id of the item that was clicked"
[& body]
`(on-item-click-call (fn [~'parent ~'view ~'position ~'id] ~@body)))

(defn on-item-long-click-call
"Takes a function and yields an AdapterView.OnItemLongClickListener object
that will invoke the function. This function must take the following four
arguments:
parent the AdapterView where the click happened
view the view within the AdapterView that was clicked
position the position of the view in the adapter
id the row id of the item that was clicked
The function should evaluate to a truthy value if it has consumed the long
click; otherwise false or nil."
[handler-fn]
{:pre [(fn? handler-fn)]
:post [(instance? android.widget.AdapterView$OnItemLongClickListener %)]}
(reify android.widget.AdapterView$OnItemLongClickListener
(onItemLongClick [this parent view position id]
(boolean (handler-fn parent view position id)))))

(defmacro on-item-long-click
"Takes a body of expressions and yields an
AdapterView.OnItemLongClickListener object that will invoke the body. The
body takes the following implicit arguments:
parent the AdapterView where the click happened
view the view within the AdapterView that was clicked
position the position of the view in the adapter
id the row id of the item that was clicked
The body should evaluate to a truthy value if it has consumed the long click;
otherwise false or nil."
[& body]
`(on-item-long-click-call (fn [~'parent ~'view ~'position ~'id] ~@body)))

(defn on-item-selected-call
"Takes one or two functions and yields an AdapterView.OnItemSelectedListener object
that will invoke the functions. The first function will be called to handle the
onItemSelected(…) method and must take the following four arguments:
parent the AdapterView where the selection happened
view the view within the AdapterView that was clicked
position the position of the view in the adapter
id the row id of the item that was selected
If a second function is provided, it will be called when the selection
disappears from the view. It takes a single argument, the AdapterView that
now contains no selected item."
([item-fn]
{:pre [(fn? item-fn)]
:post [(instance? android.widget.AdapterView$OnItemSelectedListener %)]}
(on-item-selected-call item-fn nil))
([item-fn nothing-fn]
{:pre [(fn? item-fn)
(or (nil? nothing-fn)
(fn? nothing-fn))]
:post [(instance? android.widget.AdapterView$OnItemSelectedListener %)]}
(reify android.widget.AdapterView$OnItemSelectedListener
(onItemSelected [this parent view position id]
(item-fn parent view position id))
(onNothingSelected [this parent]
(when nothing-fn
(nothing-fn parent))))))

(defmacro on-item-selected
"Takes a body of expressions and yields an AdapterView.OnItemSelectedListener
object that will invoke the body The body takes the following implicit
arguments:
type either :item corresponding an onItemSelected(…) call or :nothing
corresponding to an onNothingSelected(…) call
parent the AdapterView where the selection happened or now contains no
selected item
view the view within the AdapterView that was clicked. If type is
:nothing, this will be nil
position the position of the view in the adapter. If type is :nothing, this
will be nil.
id the row id of the item that was selected. If type is :nothing,
this will be nil."
[& body]
`(let [handler-fn# (fn [~'type ~'parent ~'view ~'position ~'id]
~@body)]
(on-item-selected-call
(fn ~'item-handler [parent# view# position# id#]
(handler-fn# :item parent# view# position# id#))
(fn ~'nothing-handler [parent#]
(handler-fn# :nothing parent# nil nil nil)))))
124 changes: 124 additions & 0 deletions test/src/clojure/neko/listeners/adapter_view_test.clj
@@ -0,0 +1,124 @@
; Copyright © 2011 Sattvik Software & Technology Resources, Ltd. Co.
; All rights reserved.
;
; This program and the accompanying materials are made available under the
; terms of the Eclipse Public License v1.0 which accompanies this distribution,
; and is available at <http://www.eclipse.org/legal/epl-v10.html>.
;
; By using this software in any fashion, you are agreeing to be bound by the
; terms of this license. You must not remove this notice, or any other, from
; this software.

(ns neko.listeners.adapter-view-test
"Tests for the neko.listeners.adapter-view namespace."
{:author "Daniel Solano Gómez"}
(:gen-class :main false
:extends android.test.AndroidTestCase
:methods [[testOnItemClick [] void]
[testOnItemLongClick [] void]
[testOnItemSelected [] void]]
:exposes-methods {setUp superSetUp})
(:import [android.widget ArrayAdapter ListView])
(:use neko.context
neko.listeners.adapter-view
junit.assert))

(def test-parent (atom nil))
(def test-adapter (atom nil))
(def test-pos 3)
(def test-view (atom nil))
(def test-id (atom nil))

(defn -setUp
"Creates new view for testing."
[this]
(.superSetUp this)
(with-context (.getContext this)
(reset! test-parent (ListView. *context*))
(reset! test-adapter
(ArrayAdapter. *context*
(get-layout :android/simple_list_item_1)
(get-id :android/text1)
(into-array ["One" "Two" "Three" "Four"]))))
(reset! test-view (.getView @test-adapter test-pos nil nil))
(reset! test-id (.getItemId @test-adapter test-pos)))

(defn -testOnItemClick
"Tests the on-item-click macro and on-item-click-call macro."
[this]
(let [count (atom 0)
fn-listener (on-item-click-call (fn [p v pos id]
(when (and (= v @test-view)
(= p @test-parent)
(= pos test-pos)
(= id @test-id))
(swap! count inc))))
macro-listener (on-item-click (when (and (= view @test-view)
(= parent @test-parent)
(= position test-pos)
(= id @test-id))
(swap! count inc)))]
(.onItemClick fn-listener @test-parent @test-view test-pos @test-id)
(.onItemClick macro-listener @test-parent @test-view test-pos @test-id)
(is-eq 2 @count)))

(defn -testOnItemLongClick
"Tests the on-item-long-click macro and on-item-long-click-call macro."
[this]
(let [fn-listener (on-item-long-click-call
(fn [p v pos id]
(if (and (= v @test-view)
(= p @test-parent)
(= pos test-pos)
(= id @test-id))
"foo!"
false)))
macro-listener (on-item-long-click
(if (and (= view @test-view)
(= parent @test-parent)
(= position test-pos)
(= id @test-id))
nil
true))]
(is (.onItemLongClick fn-listener @test-parent @test-view test-pos
@test-id))
(is-not (.onItemLongClick macro-listener @test-parent @test-view test-pos
@test-id))))

(defn -testOnItemSelected
"Tests the on-item-selected macro and on-item-selected-call macro."
[this]
(let [results (atom [])
item-fn (fn [parent view pos id]
(when (and (= parent @test-parent)
(= view @test-view)
(= pos test-pos)
(= id @test-id))
(swap! results conj :item-fn)))
item-listener (on-item-selected-call item-fn)
both-listener (on-item-selected-call
item-fn
(fn [parent]
(when (= parent @test-parent)
(swap! results conj :nothing-fn))))
macro-listener (on-item-selected
(cond
(and (= type :item)
(= parent @test-parent)
(= view @test-view)
(= position test-pos)
(= id @test-id))
(swap! results conj :item-macro)
(and (= type :nothing)
(= parent @test-parent)
(= view nil)
(= position nil)
(= id nil))
(swap! results conj :nothing-macro)))
listeners [item-listener both-listener macro-listener]]
(dorun (map #(.onItemSelected % @test-parent @test-view test-pos @test-id)
listeners))
(dorun (map #(.onNothingSelected % @test-parent)
listeners))
(is-eq [:item-fn :item-fn :item-macro :nothing-fn :nothing-macro]
@results)))

0 comments on commit f177fdf

Please sign in to comment.