From 3a572030402aacfc2ff70a64fdf105e8f370d79f Mon Sep 17 00:00:00 2001 From: nolan Date: Thu, 19 Jul 2012 16:31:37 +0000 Subject: [PATCH] Rename Handler to Presenter. --HG-- extra : convert_revision : c7d64f9263198ed8fff501ba608357d1aa326211 --- doc/scripting.wiki | 28 +-- src/main/assets/scripts/api.js | 6 +- src/main/scala/SpielService.scala | 6 +- src/main/scala/StateReactor.scala | 2 +- src/main/scala/TTS.scala | 6 +- src/main/scala/activities.scala | 2 +- .../{handlers.scala => presenters.scala} | 200 +++++++++--------- src/main/scala/scripting.scala | 44 ++-- src/main/scala/triggers.scala | 2 +- 9 files changed, 149 insertions(+), 147 deletions(-) rename src/main/scala/{handlers.scala => presenters.scala} (80%) diff --git a/doc/scripting.wiki b/doc/scripting.wiki index f0d79b05..fde878e9 100644 --- a/doc/scripting.wiki +++ b/doc/scripting.wiki @@ -14,7 +14,7 @@ The intricacies of the Android accessibility API are described exhaustively else Changes in the Android interface are described by a stream of events. When focus changes, an event is sent. When text is inserted into or deleted from a text field, an event is generated. When an incoming SMS arrives, a button is clicked or a selection is scrolled, events are generated. -Designing an Android accessibility solution involves intercepting these events and presenting them accordingly. This is precisely what Spiel does, having hard-coded handlers for a number of fundamental events. It is also what your scripts will do. +Designing an Android accessibility solution involves intercepting these events and presenting them accordingly. This is precisely what Spiel does, having hard-coded presenters for a number of fundamental events. It is also what your scripts will do. Each event has a number of properties associated with it. Not all events use every single property, and just which properties you can expect to have meaningful values depends on the event type received. @@ -47,9 +47,9 @@ For purposes of simplicity and security, scripts are divided on package boundari Scripts indicate what package they handle via their filename followed by a .js extension. For instance, a script that handles events in the com.acme.android package would be named com.acme.android.js. -

Handlers

+

Presenters

-As previously stated, all events have a class and package name with which they are associated. Events are associated with handlers, which run specific code when various criteria are matched. To attach a handler to events of a given class, the forClass method is called with the class name as its first argument and a Javascript object as its second. +As previously stated, all events have a class and package name with which they are associated. Events are associated with presenters, which run specific code when various criteria are matched. To attach a presenter to events of a given class, the forClass method is called with the class name as its first argument and a Javascript object as its second. For instance, if the app in the package com.acme.android contains a number of unlabeled buttons, then you may wish to write a script which speaks various text when said buttons receive focus. We'll discuss the contents of such a script more indepth as this manual progresses, but such a script might look like this: @@ -83,11 +83,11 @@ It is also necessary to specify which events from a given class and package are Each of these events is the name of a Javascript function which accepts the event as its first argument, and the activity name as its second. Since arguments are optional in Javascript, if the event or activity is not needed, then the function can be declared with a truncated argument list. These functions are declared in the Javascript object passed to the forClass function demonstrated above. -Spiel introduces a special handler, byDefault. This is a catch-all for events which don't yet exist but may be added in future Android versions. It is also a convenient handler for default behavior that transcends event types. +Spiel introduces a special presenter, byDefault. This is a catch-all for events which don't yet exist but may be added in future Android versions. It is also a convenient presenter for default behavior that transcends event types. -Multiple handlers can be associated with a single package and/or class. As such, these functions need to indicate if they provide all handling necessary for a given event, or if processing should continue further. They do so by returning true to indicate that handling should stop, or false if it should continue. +Multiple presenters can be associated with a single package and/or class. As such, these functions need to indicate if they provide all handling necessary for a given event, or if processing should continue further. They do so by returning true to indicate that handling should stop, or false if it should continue. -How handlers are matched is a somewhat complex subject. As a script developer, all you need know is that the handlers created in your scripts will be matched before the more generic handlers defined in Spiel. As such, in most instances you'll wish to return false from your handler functions, letting default handlers run in addition to your own. +How presenters are matched is a somewhat complex subject. As a script developer, all you need know is that the presenters created in your scripts will be matched before the more generic presenters defined in Spiel. As such, in most instances you'll wish to return false from your presenter functions, letting default presenters run in addition to your own. Building on our previous example where we wish to provide speech feedback for unlabeled buttons, we are interested in the onViewFocused event for buttons in the app's package. Since the inaccessible behavior results from focus moving to buttons with no accessible text, it makes sense that we wish to modify how such buttons are presented when they receive focus. We'll discuss how to modify the presentation later, but the below code intercepts the focus events and simply passes them on unchanged. @@ -171,9 +171,9 @@ Speaking of notifications is more complex than is simply speaking strings. The s As previously mentioned, the method by which Spiel determines whether text should interrupt is somewhat complex. It generally makes more sense to tell Spiel that a given utterance should not interrupt speech rather than that it should. -For instance, say you wish to speak a piece of text, then let event delegation continue on to core handlers. If you simply speak a string and return, Spiel will likely interrupt whatever you've spoken with its own handling of the event. If you wish to indicate that any speech generated by future handlers should not interrupt your own, however, calling nextShouldNotInterrupt() will achieve this in almost every case. +For instance, say you wish to speak a piece of text, then let event delegation continue on to core presenters. If you simply speak a string and return, Spiel will likely interrupt whatever you've spoken with its own handling of the event. If you wish to indicate that any speech generated by future presenters should not interrupt your own, however, calling nextShouldNotInterrupt() will achieve this in almost every case. -This is why scripts generally shouldn't explicitly interrupt speech without a good reason. While it is guaranteed that scripted handlers will be run before application handlers, the order in which they are run is not guaranteed. Explicitly interrupting speech disregards the needs of previously-run scripts. +This is why scripts generally shouldn't explicitly interrupt speech without a good reason. While it is guaranteed that scripted presenters will be run before application presenters, the order in which they are run is not guaranteed. Explicitly interrupting speech disregards the needs of previously-run scripts.

Event Properties

@@ -202,7 +202,7 @@ forClass("android.widget.Button" { }); -Here we do several things. As previously described, we intercept events of the given type from the package specified by the filename and the class specified in the script. In the function, we check the currentItemIndex property of the buttons, speaking a string for each which assumedly corresponds to some inaccessible label text. We then specify that the next handler should not interrupt speech and return false, stating that processing should continue. Processing then continues, eventually reaching the default button handler which speaks the string "Button". Because we have explicitly asked that the next handler not interrupt speech, our existing speech is allowed to continue and "Button" is queued. Had we not called nextShouldNotInterrupt(), the string "Button" would have interrupted our attempts to speak more accessible text, and our script would seem to have no effect. +Here we do several things. As previously described, we intercept events of the given type from the package specified by the filename and the class specified in the script. In the function, we check the currentItemIndex property of the buttons, speaking a string for each which assumedly corresponds to some inaccessible label text. We then specify that the next presenter should not interrupt speech and return false, stating that processing should continue. Processing then continues, eventually reaching the default button presenter which speaks the string "Button". Because we have explicitly asked that the next presenter not interrupt speech, our existing speech is allowed to continue and "Button" is queued. Had we not called nextShouldNotInterrupt(), the string "Button" would have interrupted our attempts to speak more accessible text, and our script would seem to have no effect.

Other Functions and Properties

@@ -216,7 +216,7 @@ Logging is an incredibly useful debugging tool. Sometimes it is helpful to know log("Here.") -from a handler in the script for the package com.acme.android will place the message "Here." into your device logs, prefixed by "com.acme.android". +from a presenter in the script for the package com.acme.android will place the message "Here." into your device logs, prefixed by "com.acme.android".

SPIEL_VERSION_CODE

@@ -244,13 +244,13 @@ It is likely that Android's behavior will change over time, with new accessibili

Script Locations

-Spiel checks for scripts in a number of locations, each with a very specific purpose. If a script read later in the loading process defines a handler with the same package and class name as one encountered earlier, that handler will override the one defined earlier. Handlers are read from the following locations: +Spiel checks for scripts in a number of locations, each with a very specific purpose. If a script read later in the loading process defines a presenter with the same package and class name as one encountered earlier, that presenter will override the one defined earlier. presenters are read from the following locations: * The assets directory in the Spiel package. This is only used to define a few core scripts whose contents should never be changed or overridden. * Spiel's internal scripts directory. Here reside various scripts received from others via the Spiel Bazaar. * The Spiel scripts directory on your SD card, typically /spiel/scripts. Here is where your own scripts should be developed before being shared with others via the Spiel Bazaar. -The main point to keep in mind is that scripts you develop or copy will always override those provided by others, or by Spiel itself. This provides you as the user with a great deal of power, but as any comic book fan knows, with great power comes great responsibility. Be aware of the scripts located on your SD card, and be sure that they don't redefine core functions, or other handlers you may not wish to be replaced. +The main point to keep in mind is that scripts you develop or copy will always override those provided by others, or by Spiel itself. This provides you as the user with a great deal of power, but as any comic book fan knows, with great power comes great responsibility. Be aware of the scripts located on your SD card, and be sure that they don't redefine core functions, or other presenters you may not wish to be replaced.

Viewing and Scripting Events

@@ -258,7 +258,7 @@ While knowing how to script is one thing, knowing which events to intercept (or When this is done, a tab labeled Events starts populating with the 50 most recently-generated accessibility events. This makes it fairly easy to determine whether a given action is raising an event and, if so, whether that event includes properties that might be used to script it. -By long-pressing an event from this list, you are presented with the option to output a script template to intercept it. The template is saved to the SD card in the scripts directory. By default, the script merely passes the event on for further handling. However, all of the event's properties are left as a comment in the script to help determine what to match. Note that this support is only intended for quick prototyping. If you use it to output several templates for a single event type, it is best to merge the handlers into a single forClass() block. +By long-pressing an event from this list, you are presented with the option to output a script template to intercept it. The template is saved to the SD card in the scripts directory. By default, the script merely passes the event on for further handling. However, all of the event's properties are left as a comment in the script to help determine what to match. Note that this support is only intended for quick prototyping. If you use it to output several templates for a single event type, it is best to merge the presenters into a single forClass() block. Events are also sent to the device log. By using the Android SDK and running the following command: @@ -321,4 +321,4 @@ forClass("android.widget.EditText", { }); -The above code is slightly more complex. Basically, we determine if the text of the current event with hyphens removed equals that of the last event received by this handler. If it does then we return true, silently swallowing the event without presenting it. Otherwise, it removes hyphens from the text of the current event and saves that as the last received text, passing the event further along the chain. The result is that hyphens will be spoken in response to some number entries, but they won't be spoken multiple times, as would occur if this script was not installed. +The above code is slightly more complex. Basically, we determine if the text of the current event with hyphens removed equals that of the last event received by this presenter. If it does then we return true, silently swallowing the event without presenting it. Otherwise, it removes hyphens from the text of the current event and saves that as the last received text, passing the event further along the chain. The result is that hyphens will be spoken in response to some number entries, but they won't be spoken multiple times, as would occur if this script was not installed. diff --git a/src/main/assets/scripts/api.js b/src/main/assets/scripts/api.js index 0ff59978..21645b6f 100644 --- a/src/main/assets/scripts/api.js +++ b/src/main/assets/scripts/api.js @@ -4,7 +4,7 @@ function speak(str, shouldInterrupt) { if(shouldInterrupt != undefined) TTS.speak(str, shouldInterrupt) else - TTS.speak(str, !Handler.nextShouldNotInterrupt()) + TTS.speak(str, !Presenter.nextShouldNotInterrupt()) return true } @@ -14,7 +14,7 @@ function speakNotification(str) { } function nextShouldNotInterrupt() { - Handler.nextShouldNotInterrupt() + Presenter.nextShouldNotInterrupt() return true } @@ -26,7 +26,7 @@ function tick() { function forClass(cls, scr) { if(cls[0] == ".") cls = __pkg__+cls - Scripter.registerHandlerFor(cls, scr) + Scripter.registerPresenterFor(cls, scr) } function addBooleanPreference(key, title, summary, dflt) { diff --git a/src/main/scala/SpielService.scala b/src/main/scala/SpielService.scala index 44285e5e..dc34d9e9 100644 --- a/src/main/scala/SpielService.scala +++ b/src/main/scala/SpielService.scala @@ -10,7 +10,7 @@ import android.view.accessibility.AccessibilityEvent import android.support.v4.app.NotificationCompat import com.nullwire.trace.ExceptionHandler -import handlers.Handler +import presenters.Presenter import scripting._ import triggers.Triggers @@ -34,7 +34,7 @@ class SpielService extends AccessibilityService { } catch { case e:VerifyError => // We've almost certainly handled this, so ignore. } - Handler(this) + Presenter(this) Scripter(this) BazaarProvider(this) StateObserver(this) @@ -78,7 +78,7 @@ class SpielService extends AccessibilityService { override def onAccessibilityEvent(event:AccessibilityEvent) { if(!SpielService.enabled) return - Handler.process(event) + Presenter.process(event) } override protected def onGesture(id:Int) = id match { diff --git a/src/main/scala/StateReactor.scala b/src/main/scala/StateReactor.scala index 77325e7c..c1b48fb8 100644 --- a/src/main/scala/StateReactor.scala +++ b/src/main/scala/StateReactor.scala @@ -267,7 +267,7 @@ object StateReactor { onUnlocked { () => if(locked) { TTS.speak(service.getString(R.string.unlocked), false) - handlers.Handler.nextShouldNotInterrupt + presenters.Presenter.nextShouldNotInterrupt locked = false } } diff --git a/src/main/scala/TTS.scala b/src/main/scala/TTS.scala index d02b1238..1aebfe92 100644 --- a/src/main/scala/TTS.scala +++ b/src/main/scala/TTS.scala @@ -12,6 +12,8 @@ import android.os.Environment import android.speech.tts.TextToSpeech import android.util.Log +import presenters.Presenter + /** * Singleton facade around TTS functionality. */ @@ -383,7 +385,7 @@ object TTS extends TextToSpeech.OnInitListener with TextToSpeech.OnUtteranceComp def speakNotification(text:String) { if(shouldSpeakNotification) { speak(text, false) - handlers.Handler.nextShouldNotInterrupt + Presenter.nextShouldNotInterrupt } } @@ -395,7 +397,7 @@ object TTS extends TextToSpeech.OnInitListener with TextToSpeech.OnUtteranceComp def speakNotification(text:List[String]) { if(shouldSpeakNotification) { speak(text, false) - handlers.Handler.nextShouldNotInterrupt + Presenter.nextShouldNotInterrupt } } diff --git a/src/main/scala/activities.scala b/src/main/scala/activities.scala index 7b060cc0..23b755e0 100644 --- a/src/main/scala/activities.scala +++ b/src/main/scala/activities.scala @@ -21,7 +21,7 @@ import android.support.v4.app.{DialogFragment, FragmentActivity, LoaderManager} import android.support.v4.content.{CursorLoader, Loader} import org.droidparts.preference.MultiSelectListPreference -import handlers._ +import presenters._ import scripting._ import triggers.Triggers diff --git a/src/main/scala/handlers.scala b/src/main/scala/presenters.scala similarity index 80% rename from src/main/scala/handlers.scala rename to src/main/scala/presenters.scala index 5c4431b1..39e61198 100644 --- a/src/main/scala/handlers.scala +++ b/src/main/scala/presenters.scala @@ -1,5 +1,5 @@ package info.spielproject.spiel -package handlers +package presenters import collection.JavaConversions._ import collection.mutable.Map @@ -42,10 +42,10 @@ class NativeCallback(f:AccessibilityEvent => Boolean) extends Callback { class PrettyAccessibilityEvent(val e:AccessibilityEvent) { - val activityName = Handler.currentActivity + val activityName = Presenter.currentActivity override val toString = { - val eventType = Handler.dispatchers.get(e.getEventType).getOrElse("Unknown") + val eventType = Presenter.dispatchers.get(e.getEventType).getOrElse("Unknown") val text = if(e.getText.length == 0) "no text: " else @@ -77,24 +77,24 @@ object EventReviewQueue extends collection.mutable.Queue[PrettyAccessibilityEven } /** - * Companion for Handler class. + * Companion for Presenter class. */ -object Handler { +object Presenter { - // Maps (packageName, className) tuples to specific Handler instances. - private var handlers = Map[(String, String), Handler]() + // Maps (packageName, className) tuples to specific Presenter instances. + private var presenters = Map[(String, String), Presenter]() /** - * Unregisters the given Handler. + * Unregisters the given Presenter. */ - def unregisterHandler(h:Handler) = { - handlers = handlers.filter(_._2 != h) + def unregisterPresenter(p:Presenter) = { + presenters = presenters.filter(_._2 != p) } def unregisterPackage(pkg:String) = { - handlers = handlers.filter(_._1._1 != pkg) + presenters = presenters.filter(_._1._1 != pkg) } // Track and report state of whether next AccessibilityEvent should interrupt speech. @@ -105,7 +105,7 @@ object Handler { /** * In some instances, speech for the next AccessibilityEvent - * shouldn't interrupt. Calling this method from a handler indicates this + * shouldn't interrupt. Calling this method from a presenter indicates this * to be the case. */ @@ -116,22 +116,22 @@ object Handler { true } - private[handlers] var context:Context = null + private[presenters] var context:Context = null private lazy val vibrator:Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE).asInstanceOf[Vibrator] /** - * Initialize handlers for the given Context. + * Initialize presenters for the given Context. */ def apply(c:Context) { context = c - // By iterating through the members of this class, we can add handlers + // By iterating through the members of this class, we can add presenters // without manual registration. - val h = new Handlers - h.getClass.getDeclaredClasses.foreach { cls => + val p = new Presenters + p.getClass.getDeclaredClasses.foreach { cls => try { - Option(cls.getConstructor(classOf[Handlers])).foreach(_.newInstance(h)) + Option(cls.getConstructor(classOf[Presenters])).foreach(_.newInstance(p)) } catch { case _ => } } } @@ -166,11 +166,11 @@ object Handler { // Now we engage in the complex process of dispatching events. This // happens in several steps. - // Store handlers we've called so we don't match them again. - var alreadyCalled = List[Handler]() + // Store presenters we've called so we don't match them again. + var alreadyCalled = List[Presenter]() - // Call the specified handler, setting state appropriately. - def dispatchTo(pkg:String, cls:String):Boolean = handlers.get(pkg -> cls).map { h => + // Call the specified Presenter, setting state appropriately. + def dispatchTo(pkg:String, cls:String):Boolean = presenters.get(pkg -> cls).map { h => if(alreadyCalled.contains(h)) { Log.d("spiel", "Already called "+h.getClass.getName+", skipping.") false @@ -181,14 +181,14 @@ object Handler { } }.getOrElse(false) - // Always run this handler before an event. This cannot block others from executing. + // Always run this Presenter before an event. This cannot block others from executing. def dispatchToBefore() = { Log.d("spiel", "Before dispatch") Before(e, eType) false } - // Let's check if there's a handler for this exact package and + // Let's check if there's a Presenter for this exact package and // class. def dispatchToExact() = { Log.d("spiel", "Exact match dispatch") @@ -202,8 +202,8 @@ object Handler { } // Now we check superclasses. Basically, if a given class is a subclass - // of a widget for which we already have a Handler (I.e. a subclass of - // Button) then it should be delegated to the handler for buttons. + // of a widget for which we already have a Presenter (I.e. a subclass of + // Button) then it should be delegated to the Presenter for buttons. // Surround this in a try block to catch the various exceptions that can // bubble up. @@ -232,7 +232,7 @@ object Handler { originator.flatMap { o => val a = ancestors(o) Log.d("spiel", "Ancestors: "+a) - val candidates = handlers.filter { h => + val candidates = presenters.filter { h => h._1._1 == "" && h._1._2 != "" && h._1._2 != "*" }.toList.map { h => val target:Class[_] = try { @@ -248,7 +248,7 @@ object Handler { }.getOrElse(false) } - // Now dispatch to the default, catch-all handler. + // Now dispatch to the default, catch-all Presenter. def dispatchToDefault() = { Log.d("spiel", "Default dispatch") dispatchTo("", "") @@ -312,14 +312,14 @@ object Handler { * Passing a blank string for either indicates events from all packages or all classes. */ -class Handler(pkg:String, cls:String) { +class Presenter(pkg:String, cls:String) { def this() = this("", "") def this(c:String) = this("", c) - import Handler._ + import Presenter._ - handlers(pkg -> cls) = this + presenters(pkg -> cls) = this // Convenience functions for calling TTS, used from scripting subsystem. @@ -364,7 +364,7 @@ class Handler(pkg:String, cls:String) { * Indicates that the next AccessibilityEvent should not interrupt speech. */ - def nextShouldNotInterrupt() = Handler.nextShouldNotInterrupt() + def nextShouldNotInterrupt() = Presenter.nextShouldNotInterrupt() // Convenience method for converting functions to callbacks. @@ -514,7 +514,7 @@ class Handler(pkg:String, cls:String) { (source :: descendantsOf(source)).filter(interactive_?(_)) /** - * Run a given AccessibilityEvent through this Handler + * Run a given AccessibilityEvent through this Presenter * * @return true if processing should stop, false otherwise. */ @@ -538,7 +538,7 @@ class Handler(pkg:String, cls:String) { * Encapsulates generic handling for multiple types of buttons. */ -trait GenericButtonHandler extends Handler { +trait GenericButtonPresenter extends Presenter { onViewFocused { e:AccessibilityEvent => val text = utterancesFor(e, addBlank=false).mkString(": ") if(text == "") { @@ -548,23 +548,23 @@ trait GenericButtonHandler extends Handler { val descendants = descendantsOf(root) val index = descendants.indexOf(source)+1 if(index > 0) - speak(Handler.context.getString(R.string.listItem, Handler.context.getText(R.string.button), index.toString, descendants.size.toString)) + speak(Presenter.context.getString(R.string.listItem, Presenter.context.getText(R.string.button), index.toString, descendants.size.toString)) else None } - }.getOrElse(speak(Handler.context.getString(R.string.button).toString)) + }.getOrElse(speak(Presenter.context.getString(R.string.button).toString)) true } else - speak(Handler.context.getString(R.string.button).toString) + speak(Presenter.context.getString(R.string.button).toString) } else - speak(Handler.context.getString(R.string.labeledButton, text)) + speak(Presenter.context.getString(R.string.labeledButton, text)) } } /** - * Run before every event. Cannot pre-empt other handlers. + * Run before every event. Cannot pre-empt other Presenters. */ -object Before extends Handler("*") { +object Before extends Presenter("*") { onViewHoverEnter { e:AccessibilityEvent => stopSpeaking() @@ -597,13 +597,13 @@ object Before extends Handler("*") { * Run after every event. */ -object After extends Handler("", "*") { +object After extends Presenter("", "*") { onViewFocused { e:AccessibilityEvent => if(VERSION.SDK_INT >= 14) Option(e.getSource).foreach { source => if(source.getChildCount == 0 && interactive_?(source) && !e.isEnabled) - speak(Handler.context.getString(R.string.disabled), false) + speak(Presenter.context.getString(R.string.disabled), false) } false } @@ -611,35 +611,35 @@ object After extends Handler("", "*") { } /** - * By placing all Handler classes here, we can use the power of + * By placing all Presenter classes here, we can use the power of * reflection to avoid manually registering each and every one. */ -class Handlers { +class Presenters { - class ActionMenuItemView extends Handler("com.android.internal.view.menu.ActionMenuItemView") { + class ActionMenuItemView extends Presenter("com.android.internal.view.menu.ActionMenuItemView") { onViewFocused { e:AccessibilityEvent => speak(utterancesFor(e, stripBlanks=true) ::: ("Menu item" :: Nil)) } - onViewHoverEnter { e:AccessibilityEvent => Handler.process(e, Some(TYPE_VIEW_FOCUSED)) } + onViewHoverEnter { e:AccessibilityEvent => Presenter.process(e, Some(TYPE_VIEW_FOCUSED)) } } - class AdapterView extends Handler("android.widget.AdapterView") { + class AdapterView extends Presenter("android.widget.AdapterView") { private def focusedOnList(e:AccessibilityEvent) = { val utterances = utterancesFor(e, stripBlanks = true) if(utterances != Nil && e.getCurrentItemIndex != -1) - speak(Handler.context.getString(R.string.listItem, utterances.mkString(": "), (e.getCurrentItemIndex+1).toString, e.getItemCount.toString)) + speak(Presenter.context.getString(R.string.listItem, utterances.mkString(": "), (e.getCurrentItemIndex+1).toString, e.getItemCount.toString)) else if(e.getItemCount == 0) - speak(Handler.context.getString(R.string.emptyList)) + speak(Presenter.context.getString(R.string.emptyList)) else if(e.getItemCount == 1) - speak(Handler.context.getString(R.string.listWithItem)) + speak(Presenter.context.getString(R.string.listWithItem)) else if(e.getItemCount > 1) - speak(Handler.context.getString(R.string.listWithItems, e.getItemCount.toString)) + speak(Presenter.context.getString(R.string.listWithItems, e.getItemCount.toString)) true } @@ -655,9 +655,9 @@ class Handlers { onViewSelected { e:AccessibilityEvent => if(e.getCurrentItemIndex >= 0) - speak(Handler.context.getString(R.string.listItem, utterancesFor(e).mkString(": "), (e.getCurrentItemIndex+1).toString, e.getItemCount.toString)) + speak(Presenter.context.getString(R.string.listItem, utterancesFor(e).mkString(": "), (e.getCurrentItemIndex+1).toString, e.getItemCount.toString)) else if(e.getItemCount == 0) - speak(Handler.context.getString(R.string.emptyList)) + speak(Presenter.context.getString(R.string.emptyList)) true } @@ -665,47 +665,47 @@ class Handlers { } - class AlertDialog extends Handler("android.app.AlertDialog") { + class AlertDialog extends Presenter("android.app.AlertDialog") { onWindowStateChanged { e:AccessibilityEvent => - speak(Handler.context.getString(R.string.alert, utterancesFor(e, stripBlanks=true).mkString(": ")), true) + speak(Presenter.context.getString(R.string.alert, utterancesFor(e, stripBlanks=true).mkString(": ")), true) nextShouldNotInterrupt() } } - class Button extends Handler("android.widget.Button") with GenericButtonHandler + class Button extends Presenter("android.widget.Button") with GenericButtonPresenter - class CheckBox extends Handler("android.widget.CheckBox") { + class CheckBox extends Presenter("android.widget.CheckBox") { onViewClicked { e:AccessibilityEvent => if(e.isChecked) - speak(Handler.context.getString(R.string.checked)) + speak(Presenter.context.getString(R.string.checked)) else - speak(Handler.context.getString(R.string.notChecked)) + speak(Presenter.context.getString(R.string.notChecked)) } onViewFocused { e:AccessibilityEvent => - speak(Handler.context.getString(R.string.checkbox, utterancesFor(e, addBlank=false, guessLabelIfTextShorterThan = Some(2)).mkString(": "))) + speak(Presenter.context.getString(R.string.checkbox, utterancesFor(e, addBlank=false, guessLabelIfTextShorterThan = Some(2)).mkString(": "))) } } - class Dialog extends Handler("android.app.Dialog") { + class Dialog extends Presenter("android.app.Dialog") { onWindowStateChanged { e:AccessibilityEvent => speak(utterancesFor(e), true) nextShouldNotInterrupt() } } - class EditText extends Handler("android.widget.EditText") { + class EditText extends Presenter("android.widget.EditText") { onViewFocused { e:AccessibilityEvent => if(e.isPassword) { speak(utterancesFor(e, addBlank=false, guessLabelIfContentDescriptionMissing = true), false) - speak(Handler.context.getString(R.string.password)) - speak(Handler.context.getString(R.string.editText), false) + speak(Presenter.context.getString(R.string.password)) + speak(Presenter.context.getString(R.string.editText), false) } else { speak(utterancesFor(e, addBlank=true, guessLabelIfContentDescriptionMissing = true), false) - speak(Handler.context.getString(R.string.editText), false) + speak(Presenter.context.getString(R.string.editText), false) } true } @@ -758,13 +758,13 @@ class Handlers { } trait MenuView { - self: Handler => - onViewFocused { e:AccessibilityEvent => speak(Handler.context.getString(R.string.menu)) } + self: Presenter => + onViewFocused { e:AccessibilityEvent => speak(Presenter.context.getString(R.string.menu)) } } - class ExpandedMenuView extends Handler("com.android.internal.view.menu.ExpandedMenuView") with MenuView + class ExpandedMenuView extends Presenter("com.android.internal.view.menu.ExpandedMenuView") with MenuView - class HomeView extends Handler("com.android.internal.widget.ActionBarView$HomeView") { + class HomeView extends Presenter("com.android.internal.widget.ActionBarView$HomeView") { private def process(e:AccessibilityEvent) = { val utterances = utterancesFor(e, addBlank = false) @@ -779,34 +779,34 @@ class Handlers { } - class ImageButton extends Handler("android.widget.ImageButton") with GenericButtonHandler + class ImageButton extends Presenter("android.widget.ImageButton") with GenericButtonPresenter - class ImageView extends Handler("android.widget.ImageView") { + class ImageView extends Presenter("android.widget.ImageView") { onViewFocused { e:AccessibilityEvent => val text = utterancesFor(e, addBlank=false).mkString(": ") if(text == "") if(e.getItemCount > 0 && e.getCurrentItemIndex >= 0) - speak(Handler.context.getString(R.string.listItem, Handler.context.getText(R.string.image), (e.getCurrentItemIndex+1).toString, e.getItemCount.toString)) + speak(Presenter.context.getString(R.string.listItem, Presenter.context.getText(R.string.image), (e.getCurrentItemIndex+1).toString, e.getItemCount.toString)) else if(VERSION.SDK_INT >= 14 && e.getSource != null) { val source = e.getSource rootOf(source).map { root => val descendants = descendantsOf(root) val index = descendants.indexOf(source)+1 if(index > 0) - speak(Handler.context.getString(R.string.listItem, Handler.context.getText(R.string.image), index.toString, descendants.length.toString)) + speak(Presenter.context.getString(R.string.listItem, Presenter.context.getText(R.string.image), index.toString, descendants.length.toString)) else - speak(Handler.context.getText(R.string.image).toString) - }.getOrElse(speak(Handler.context.getText(R.string.image).toString)) + speak(Presenter.context.getText(R.string.image).toString) + }.getOrElse(speak(Presenter.context.getText(R.string.image).toString)) } else - speak(Handler.context.getText(R.string.image).toString) + speak(Presenter.context.getText(R.string.image).toString) else - speak(Handler.context.getString(R.string.labeledImage, text)) + speak(Presenter.context.getString(R.string.labeledImage, text)) } } - class IconMenuView extends Handler("com.android.internal.view.menu.IconMenuView") with MenuView + class IconMenuView extends Presenter("com.android.internal.view.menu.IconMenuView") with MenuView - class Menu extends Handler("com.android.internal.view.menu.MenuView") { + class Menu extends Presenter("com.android.internal.view.menu.MenuView") { onViewSelected { e:AccessibilityEvent => speak(utterancesFor(e)) @@ -814,7 +814,7 @@ class Handlers { onWindowStateChanged { e:AccessibilityEvent => if(e.getCurrentItemIndex == -1) { - speak(Handler.context.getString(R.string.menu), true) + speak(Presenter.context.getString(R.string.menu), true) nextShouldNotInterrupt() } true @@ -822,7 +822,7 @@ class Handlers { } - class ProgressBar extends Handler("android.widget.ProgressBar") { + class ProgressBar extends Presenter("android.widget.ProgressBar") { onViewFocused { e:AccessibilityEvent => val percent = (e.getCurrentItemIndex.toDouble/e.getItemCount*100).toInt @@ -836,26 +836,26 @@ class Handlers { } - class RadioButton extends Handler("android.widget.RadioButton") { + class RadioButton extends Presenter("android.widget.RadioButton") { onViewClicked { e:AccessibilityEvent => if(e.isChecked) - speak(Handler.context.getString(R.string.selected)) + speak(Presenter.context.getString(R.string.selected)) else - speak(Handler.context.getString(R.string.notSelected)) + speak(Presenter.context.getString(R.string.notSelected)) } onViewFocused { e:AccessibilityEvent => - speak(Handler.context.getString(R.string.radioButton, utterancesFor(e, guessLabelIfTextShorterThan = Some(2)).mkString(": "))) + speak(Presenter.context.getString(R.string.radioButton, utterancesFor(e, guessLabelIfTextShorterThan = Some(2)).mkString(": "))) } } - class RatingBar extends Handler("android.widget.RatingBar") { + class RatingBar extends Presenter("android.widget.RatingBar") { onViewFocused { e:AccessibilityEvent => - val label = guessLabelFor(e).getOrElse(Handler.context.getString(R.string.rating)) - val rating = Handler.context.getString(R.string.listItem, label, e.getCurrentItemIndex.toString, e.getItemCount.toString) + val label = guessLabelFor(e).getOrElse(Presenter.context.getString(R.string.rating)) + val rating = Presenter.context.getString(R.string.listItem, label, e.getCurrentItemIndex.toString, e.getItemCount.toString) speak(utterancesFor(e, addBlank = false, providedText=Some(rating))) } @@ -871,7 +871,7 @@ class Handlers { } - class ScrollView extends Handler("android.widget.ScrollView") { + class ScrollView extends Presenter("android.widget.ScrollView") { onViewFocused { e:AccessibilityEvent => true } @@ -888,17 +888,17 @@ class Handlers { } - class SearchBox extends Handler("android.app.SearchDialog$SearchAutoComplete") { + class SearchBox extends Presenter("android.app.SearchDialog$SearchAutoComplete") { onViewFocused { e:AccessibilityEvent => - speak(Handler.context.getString(R.string.searchText, utterancesFor(e).mkString(": ")), false) + speak(Presenter.context.getString(R.string.searchText, utterancesFor(e).mkString(": ")), false) } } - class TextView extends Handler("android.widget.TextView") { + class TextView extends Presenter("android.widget.TextView") { onViewFocused { e:AccessibilityEvent => speak(utterancesFor(e, stripBlanks=true)) } } - class ViewGroup extends Handler("android.view.ViewGroup") { + class ViewGroup extends Presenter("android.view.ViewGroup") { onViewFocused { e:AccessibilityEvent => if(VERSION.SDK_INT >= 14) { @@ -939,7 +939,7 @@ class Handlers { } - class WebView extends Handler("android.webkit.WebView") { + class WebView extends Presenter("android.webkit.WebView") { private def utterancesFor(x:xml.Node):List[String] = { @@ -949,7 +949,7 @@ class Handlers { def recurse(nodes:List[xml.Node]):List[String] = nodes match { case Nil => Nil case hd :: tl if(name(hd) == "a" && hd.text != null) => - hd.text :: Handler.context.getString(R.string.link) :: Nil + hd.text :: Presenter.context.getString(R.string.link) :: Nil case hd :: tl if(hd.descendant.size == 0 && hd.text != null && hd.text != "") => hd.text :: recurse(tl) case hd :: tl => recurse(tl) @@ -978,10 +978,10 @@ class Handlers { } /** - * Default catch-all handler which catches unresolved AccessibilityEvents. + * Default catch-all Presenter which catches unresolved AccessibilityEvents. */ - class Default extends Handler { + class Default extends Presenter { onNotificationStateChanged { e:AccessibilityEvent => val utterances = utterancesFor(e, addBlank=false, stripBlanks=true) @@ -1009,7 +1009,7 @@ class Handlers { true } - onViewHoverEnter { e:AccessibilityEvent => Handler.process(e, Some(TYPE_VIEW_FOCUSED)) } + onViewHoverEnter { e:AccessibilityEvent => Presenter.process(e, Some(TYPE_VIEW_FOCUSED)) } onViewHoverExit { e:AccessibilityEvent => true } @@ -1029,9 +1029,9 @@ class Handlers { if(utterances.length > 0) { if(e.getCurrentItemIndex == -1) if(e.getItemCount == 1) - speak(Handler.context.getString(R.string.item, utterances.mkString(" "))) + speak(Presenter.context.getString(R.string.item, utterances.mkString(" "))) else if(e.getItemCount >= 0) - speak(Handler.context.getString(R.string.items, utterances.mkString(" "), e.getItemCount.toString)) + speak(Presenter.context.getString(R.string.items, utterances.mkString(" "), e.getItemCount.toString)) else speak(utterances) else diff --git a/src/main/scala/scripting.scala b/src/main/scala/scripting.scala index f8b9b7f2..cefffa7a 100644 --- a/src/main/scala/scripting.scala +++ b/src/main/scala/scripting.scala @@ -4,7 +4,7 @@ package scripting import java.io.{File, FileInputStream, FileOutputStream, FileWriter, InputStream} import java.lang.Integer -import handlers.PrettyAccessibilityEvent +import presenters.PrettyAccessibilityEvent import android.content.{BroadcastReceiver, ContentValues, Context => AContext, Intent} import android.content.pm.PackageManager @@ -16,17 +16,17 @@ import android.view.accessibility.AccessibilityEvent import org.mozilla.javascript.{Context, ContextFactory, Function, RhinoException, ScriptableObject} -import handlers.{Callback, Handler} +import presenters.{Callback, Presenter} /** - * Handler callback that executes a Rhino script. + * Presenter callback that executes a Rhino script. */ class RhinoCallback(f:Function) extends Callback { def apply(e:AccessibilityEvent):Boolean = { var args = new Array[Object](2) args(0) = e - args(1) = Handler.currentActivity + args(1) = Presenter.currentActivity ContextFactory.getGlobal.enterContext(Scripter.context) try { Scripter.scope.put("__pkg__", Scripter.scope, e.getPackageName) @@ -110,15 +110,15 @@ class Script( successfullyRan } - private var handlers = List[Handler]() + private var presenters = List[Presenter]() /** - * Register a Handler for a given package and class. + * Register a Presenter for a given package and class. */ - def registerHandlerFor(cls:String, s:Object) { + def registerPresenterFor(cls:String, s:Object) { val scr = s.asInstanceOf[ScriptableObject] - val h = new Handler(pkg, cls) + val p = new Presenter(pkg, cls) scr.getIds.foreach { property => @@ -132,18 +132,18 @@ class Script( // Check to ensure that the property name maps to a valid AccessibilityEvent type. - if(Handler.dispatchers.valuesIterator.contains(func)) { + if(Presenter.dispatchers.valuesIterator.contains(func)) { val f = scr.get(id, Scripter.scope) if(f.isInstanceOf[Function]) // Map the script to an Android AccessibilityEvent type. - h.dispatches(func) = new RhinoCallback(f.asInstanceOf[Function]) + p.dispatches(func) = new RhinoCallback(f.asInstanceOf[Function]) } else - Log.e("spiel", func+" is not a valid handler. Skipping.") + Log.e("spiel", func+" is not a valid presenter. Skipping.") } - handlers ::= h + presenters ::= p - h + p } def save() { @@ -200,9 +200,9 @@ class Script( } def uninstall() { - handlers.foreach(Handler.unregisterHandler(_)) - Handler.unregisterPackage(pkg) - handlers = Nil + presenters.foreach(Presenter.unregisterPresenter(_)) + Presenter.unregisterPackage(pkg) + presenters = Nil Scripter.unsetStringsFor(pkg) Scripter.removePreferencesFor(pkg) } @@ -236,7 +236,7 @@ class Observer(context:AContext, path:String) extends FileObserver(path) { import FileObserver._ def onEvent(event:Int, path:String) = event match { case CREATE | MODIFY | MOVED_TO => (new Script(context, path, false)).reload() - case DELETE | MOVED_FROM => Handler.unregisterPackage(path.split("\\.").head) + case DELETE | MOVED_FROM => Presenter.unregisterPackage(path.split("\\.").head) case _ => } } @@ -271,8 +271,8 @@ object Scripter { context.setOptimizationLevel(-1) // Inject some Spiel objects into the scripting environment. - val wrappedHandler = Context.javaToJS(Handler, scope) - ScriptableObject.putProperty(scope, "Handler", wrappedHandler) + val wrappedPresenter = Context.javaToJS(Presenter, scope) + ScriptableObject.putProperty(scope, "Presenter", wrappedPresenter) val wrappedScripter = Context.javaToJS(this, scope) ScriptableObject.putProperty(scope, "Scripter", wrappedScripter) @@ -335,7 +335,7 @@ object Scripter { }.getOrElse(Nil) } - def registerHandlerFor(cls:String, scr:Object) = script.map(_.registerHandlerFor(cls, scr)) + def registerPresenterFor(cls:String, scr:Object) = script.map(_.registerPresenterFor(cls, scr)) private var _preferences = Map[String, Map[String, Map[String, Any]]]() @@ -414,8 +414,8 @@ object Scripter { def createTemplateFor(event:PrettyAccessibilityEvent) = { try { - val handler = "on"+Handler.dispatchers(event.e.getEventType).capitalize - val code = "forClass(\""+event.e.getClassName+"\", {\n "+handler+": function(e, activity) {\n // "+event.toString+"\n return false\n }\n})\n" + val presenter = "on"+Presenter.dispatchers(event.e.getEventType).capitalize + val code = "forClass(\""+event.e.getClassName+"\", {\n "+presenter+": function(e, activity) {\n // "+event.toString+"\n return false\n }\n})\n" val file = new File(scriptsDir, event.e.getPackageName+".js") val writer = new FileWriter(file, true) writer.write(code) diff --git a/src/main/scala/triggers.scala b/src/main/scala/triggers.scala index 3fb4228d..c4fb5310 100644 --- a/src/main/scala/triggers.scala +++ b/src/main/scala/triggers.scala @@ -5,7 +5,7 @@ import android.content.{BroadcastReceiver, Context, Intent} import android.content.pm.PackageManager import android.util.Log -import handlers.EventReviewQueue +import presenters.EventReviewQueue import scripting.Scripter /**