@@ -97,9 +97,12 @@ class AccessibilityInjector {
9797 // Template for JavaScript that performs AndroidVox actions.
9898 private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
9999 "(function() {" +
100- " if ((typeof(cvox) != 'undefined')" +
100+ " if ((typeof(cvox) != 'undefined')" +
101+ " && (cvox != null)" +
101102 " && (typeof(cvox.ChromeVox) != 'undefined')" +
103+ " && (cvox.ChromeVox != null)" +
102104 " && (typeof(cvox.AndroidVox) != 'undefined')" +
105+ " && (cvox.AndroidVox != null)" +
103106 " && cvox.ChromeVox.isActive) {" +
104107 " return cvox.AndroidVox.performAction('%1s');" +
105108 " } else {" +
@@ -110,9 +113,12 @@ class AccessibilityInjector {
110113 // JS code used to shut down an active AndroidVox instance.
111114 private static final String TOGGLE_CVOX_TEMPLATE =
112115 "javascript:(function() {" +
113- " if ((typeof(cvox) != 'undefined')" +
116+ " if ((typeof(cvox) != 'undefined')" +
117+ " && (cvox != null)" +
114118 " && (typeof(cvox.ChromeVox) != 'undefined')" +
115- " && (typeof(cvox.ChromeVox.host) != 'undefined')) {" +
119+ " && (cvox.ChromeVox != null)" +
120+ " && (typeof(cvox.ChromeVox.host) != 'undefined')" +
121+ " && (cvox.ChromeVox.host != null)) {" +
116122 " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" +
117123 " }" +
118124 "})();" ;
@@ -131,34 +137,61 @@ public AccessibilityInjector(WebViewClassic webViewClassic) {
131137 mAccessibilityManager = AccessibilityManager .getInstance (mContext );
132138 }
133139
140+ /**
141+ * If JavaScript is enabled, pauses or resumes AndroidVox.
142+ *
143+ * @param enabled Whether feedback should be enabled.
144+ */
145+ public void toggleAccessibilityFeedback (boolean enabled ) {
146+ if (!isAccessibilityEnabled () || !isJavaScriptEnabled ()) {
147+ return ;
148+ }
149+
150+ toggleAndroidVox (enabled );
151+
152+ if (!enabled && (mTextToSpeech != null )) {
153+ mTextToSpeech .stop ();
154+ }
155+ }
156+
134157 /**
135158 * Attempts to load scripting interfaces for accessibility.
136159 * <p>
137- * This should be called when the window is attached.
138- * </p>
160+ * This should only be called before a page loads.
139161 */
140- public void addAccessibilityApisIfNecessary () {
162+ private void addAccessibilityApisIfNecessary () {
141163 if (!isAccessibilityEnabled () || !isJavaScriptEnabled ()) {
142164 return ;
143165 }
144166
145167 addTtsApis ();
146168 addCallbackApis ();
147- toggleAndroidVox (true );
148169 }
149170
150171 /**
151172 * Attempts to unload scripting interfaces for accessibility.
152173 * <p>
153- * This should be called when the window is detached.
154- * </p>
174+ * This should only be called before a page loads.
155175 */
156- public void removeAccessibilityApisIfNecessary () {
157- toggleAndroidVox (false );
176+ private void removeAccessibilityApisIfNecessary () {
158177 removeTtsApis ();
159178 removeCallbackApis ();
160179 }
161180
181+ /**
182+ * Destroys this accessibility injector.
183+ */
184+ public void destroy () {
185+ if (mTextToSpeech != null ) {
186+ mTextToSpeech .shutdown ();
187+ mTextToSpeech = null ;
188+ }
189+
190+ if (mCallback != null ) {
191+ mCallback = null ;
192+ }
193+ }
194+
162195 private void toggleAndroidVox (boolean state ) {
163196 if (!mAccessibilityScriptInjected ) {
164197 return ;
@@ -517,7 +550,12 @@ private String getScreenReaderInjectionUrl() {
517550 * settings.
518551 */
519552 private boolean isJavaScriptEnabled () {
520- return mWebView .getSettings ().getJavaScriptEnabled ();
553+ final WebSettings settings = mWebView .getSettings ();
554+ if (settings == null ) {
555+ return false ;
556+ }
557+
558+ return settings .getJavaScriptEnabled ();
521559 }
522560
523561 /**
@@ -732,7 +770,7 @@ private static class CallbackHandler {
732770 private final String mInterfaceName ;
733771
734772 private boolean mResult = false ;
735- private long mResultId = -1 ;
773+ private int mResultId = -1 ;
736774
737775 private CallbackHandler (String interfaceName ) {
738776 mInterfaceName = interfaceName ;
@@ -784,34 +822,46 @@ private void clearResultLocked() {
784822 * @return Whether the result was received.
785823 */
786824 private boolean waitForResultTimedLocked (int resultId ) {
787- if (DEBUG )
788- Log .d (TAG , "Waiting for CVOX result..." );
789- long waitTimeMillis = RESULT_TIMEOUT ;
790825 final long startTimeMillis = SystemClock .uptimeMillis ();
826+
827+ if (DEBUG )
828+ Log .d (TAG , "Waiting for CVOX result with ID " + resultId + "..." );
829+
791830 while (true ) {
831+ // Fail if we received a callback from the future.
832+ if (mResultId > resultId ) {
833+ if (DEBUG )
834+ Log .w (TAG , "Aborted CVOX result" );
835+ return false ;
836+ }
837+
838+ final long elapsedTimeMillis = (SystemClock .uptimeMillis () - startTimeMillis );
839+
840+ // Succeed if we received the callback we were expecting.
841+ if (DEBUG )
842+ Log .w (TAG , "Check " + mResultId + " versus expected " + resultId );
843+ if (mResultId == resultId ) {
844+ if (DEBUG )
845+ Log .w (TAG , "Received CVOX result after " + elapsedTimeMillis + " ms" );
846+ return true ;
847+ }
848+
849+ final long waitTimeMillis = (RESULT_TIMEOUT - elapsedTimeMillis );
850+
851+ // Fail if we've already exceeded the timeout.
852+ if (waitTimeMillis <= 0 ) {
853+ if (DEBUG )
854+ Log .w (TAG , "Timed out while waiting for CVOX result" );
855+ return false ;
856+ }
857+
792858 try {
793- if (mResultId == resultId ) {
794- if (DEBUG )
795- Log .w (TAG , "Received CVOX result" );
796- return true ;
797- }
798- if (mResultId > resultId ) {
799- if (DEBUG )
800- Log .w (TAG , "Obsolete CVOX result" );
801- return false ;
802- }
803- final long elapsedTimeMillis = SystemClock .uptimeMillis () - startTimeMillis ;
804- waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis ;
805- if (waitTimeMillis <= 0 ) {
806- if (DEBUG )
807- Log .w (TAG , "Timed out while waiting for CVOX result" );
808- return false ;
809- }
859+ if (DEBUG )
860+ Log .w (TAG , "Start waiting..." );
810861 mResultLock .wait (waitTimeMillis );
811862 } catch (InterruptedException ie ) {
812863 if (DEBUG )
813864 Log .w (TAG , "Interrupted while waiting for CVOX result" );
814- /* ignore */
815865 }
816866 }
817867 }
@@ -827,11 +877,11 @@ private boolean waitForResultTimedLocked(int resultId) {
827877 @ SuppressWarnings ("unused" )
828878 public void onResult (String id , String result ) {
829879 if (DEBUG )
830- Log .w (TAG , "Saw CVOX result of '" + result + "'" );
831- final long resultId ;
880+ Log .w (TAG , "Saw CVOX result of '" + result + "' for ID " + id );
881+ final int resultId ;
832882
833883 try {
834- resultId = Long . parseLong (id );
884+ resultId = Integer . parseInt (id );
835885 } catch (NumberFormatException e ) {
836886 return ;
837887 }
@@ -840,6 +890,9 @@ public void onResult(String id, String result) {
840890 if (resultId > mResultId ) {
841891 mResult = Boolean .parseBoolean (result );
842892 mResultId = resultId ;
893+ } else {
894+ if (DEBUG )
895+ Log .w (TAG , "Result with ID " + resultId + " was stale vesus " + mResultId );
843896 }
844897 mResultLock .notifyAll ();
845898 }
0 commit comments