From 9f93186beae13ea057a4b2e867928b357cd650a4 Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Tue, 10 Aug 2010 21:41:06 -0700 Subject: [PATCH] Adding a sample accessibility service Change-Id: I5bd42157e263aac6d7a85db090319db168dea2fd --- build/sdk.atree | 1 + samples/AccessibilityService/Android.mk | 12 + .../AccessibilityService/AndroidManifest.xml | 40 + samples/AccessibilityService/_index.html | 120 +++ .../res/raw/sound_ringer_normal.ogg | Bin 0 -> 8041 bytes .../res/raw/sound_ringer_silent.ogg | Bin 0 -> 16821 bytes .../res/raw/sound_ringer_vibrate.ogg | Bin 0 -> 8647 bytes .../res/raw/sound_screen_off.ogg | Bin 0 -> 5772 bytes .../res/raw/sound_screen_on.ogg | Bin 0 -> 13311 bytes .../res/raw/sound_view_clicked.ogg | Bin 0 -> 5180 bytes .../raw/sound_view_focused_or_selected.ogg | Bin 0 -> 6773 bytes .../res/raw/sound_window_state_changed.ogg | Bin 0 -> 3938 bytes .../res/values/strings.xml | 50 ++ .../android/clockback/ClockBackService.java | 701 ++++++++++++++++++ 14 files changed, 924 insertions(+) create mode 100644 samples/AccessibilityService/Android.mk create mode 100644 samples/AccessibilityService/AndroidManifest.xml create mode 100644 samples/AccessibilityService/_index.html create mode 100644 samples/AccessibilityService/res/raw/sound_ringer_normal.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_ringer_silent.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_ringer_vibrate.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_screen_off.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_screen_on.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_view_clicked.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_view_focused_or_selected.ogg create mode 100644 samples/AccessibilityService/res/raw/sound_window_state_changed.ogg create mode 100644 samples/AccessibilityService/res/values/strings.xml create mode 100644 samples/AccessibilityService/src/com/example/android/clockback/ClockBackService.java diff --git a/build/sdk.atree b/build/sdk.atree index 4b76de4b6..eda4256eb 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -86,6 +86,7 @@ sdk/files/android.el tools/lib/android.el # (see web_docs_sample_code_flags in frameworks/base/Android.mk) development/samples/source.properties samples/${PLATFORM_NAME}/source.properties development/apps/GestureBuilder samples/${PLATFORM_NAME}/GestureBuilder +development/samples/AccessibilityService samples/${PLATFORM_NAME}/AccessibilityService development/samples/BluetoothChat samples/${PLATFORM_NAME}/BluetoothChat development/samples/Home samples/${PLATFORM_NAME}/Home development/samples/LunarLander samples/${PLATFORM_NAME}/LunarLander diff --git a/samples/AccessibilityService/Android.mk b/samples/AccessibilityService/Android.mk new file mode 100644 index 000000000..e47b6f605 --- /dev/null +++ b/samples/AccessibilityService/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := samples + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := AccessibilityService + +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) diff --git a/samples/AccessibilityService/AndroidManifest.xml b/samples/AccessibilityService/AndroidManifest.xml new file mode 100644 index 000000000..1b5f7944a --- /dev/null +++ b/samples/AccessibilityService/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AccessibilityService/_index.html b/samples/AccessibilityService/_index.html new file mode 100644 index 000000000..d6adbf2c7 --- /dev/null +++ b/samples/AccessibilityService/_index.html @@ -0,0 +1,120 @@ +

+ This is an example of an accessibility service that provides custom feedback for the Clock application which comes by default with Android devices. It demonstrates the following key features of the Android accessibility APIs: +

+
    +
  1. + Simple demonstration of how to use the accessibility APIs. +
  2. +
  3. + Hands-on example of various ways to utilize the accessibility API for providing alternative and complementary feedback. +
  4. +
  5. + Providing application specific feedback — the service handles only accessibility events from the Clock application. +
  6. +
  7. + Providing dynamic, context-dependent feedback — feedback type changes depending on the ringer mode. +
  8. +
  9. + Application specific UI enhancement — application domain knowledge is utilized to enhance the provided feedback. +
  10. +
+

+ + Note: This code sample will work only on devices shipped with the default Clock application. If you are + running Android 1.6 of Android 2.0 you should enable first ClockBack and then TalkBack since in these + releases accessibility services are notified in the order of registration. + +

+

+ Steps to exercise the ClockBack example: +

+
    +
  1. +
      +
    • + Action: Enable accessibility and all default accessibility services:
      + Settings → Accessibility → select the Accessibility, TalkBack, KickBack, and SoundBack checkboxes +
    • +
    • + Result: The system provides spoken, audible, and haptic feedback. +
    • +
    +
  2. +
  3. +
      +
    • + Action: Explore the feedback provided by the system:
      + Poke around with the trackball. +
    • +
    • + Result: You are somehow familiar with the type of the provided feedback. +
    • +
    +
  4. +
  5. +
      +
    • + Action: Go to the Clock application and try to change the time of an alarm:
      + All applications → Clock → Alarms (left corner) → Select an alarm → Time — explore the plus, minus buttons, hour and minute edit boxes. +
    • +
    • + Result: The hour and minute edit boxes are announced without any clue which is the hour and which is the minute one (you can guess from the arrangement). +
    • +
    +
  6. +
  7. +
      +
    • + Action: Enable ClockBack:
      + Settings → Accessibility → ClockBack — select the checkbox (assuming you have installed the APK). +
    • +
    • + Result: We have active accessibility service for providing application specific feedback for the Clock application. +
    • +
    +
  8. +
  9. +
      +
    • + Action: Go to the Clock application and try to change the time of an alarm:
      + All applications → Clock → Alarms (left corner) → Select an alarm → Time — explore the hour and minute edit boxes. +
    • +
    • + Result: The hour and minute edit boxes are now spoken. This is an example of application specific feedback that utilizes domain information to enhance the user experience. +
    • +
    +
  10. +
  11. +
      +
    • + Action: Set the ringer to vibration mode and explore the provided feedback:
      + Use the device button for reducing the ringer volume until it is in vibration mode. Move around the Clock application and outside of that application. +
    • +
    • + Result: The Clock application provides custom audible and default haptic feedback. The rest of the system provides the default feedback. +
    • +
    +
  12. +
  13. +
      +
    • + Action: Set the ringer to muted mode and explore the provided feedback:
      + Use the device button for reducing the ringer volume until it is in muted mode. Move around the Clock application and outside of that application. +
    • +
    • + Result: The Clock application provides only custom haptic feedback. The rest of the system provides the default feedback. Now we are providing custom context dependent feedback based on the device state (ringer mode). +
    • +
    +
  14. +
  15. +
      +
    • + Action: Write an accessibility service:
      + The Eyes-Free open source project has more accessibility-related resources. To contribute, visit the project page or post to the mailing list. +
    • +
    • + Result: One more cool application has been written. +
    • +
    +
  16. +
diff --git a/samples/AccessibilityService/res/raw/sound_ringer_normal.ogg b/samples/AccessibilityService/res/raw/sound_ringer_normal.ogg new file mode 100644 index 0000000000000000000000000000000000000000..959e398def561ce32d38707eef0fb790a82ed5c2 GIT binary patch literal 8041 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 Ax&QzG literal 0 HcmV?d00001 diff --git a/samples/AccessibilityService/res/raw/sound_ringer_silent.ogg b/samples/AccessibilityService/res/raw/sound_ringer_silent.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d3bb25efa8d0ab9ce60f1a5f91218a871fe15ac6 GIT binary patch literal 16821 zcmeIZcT^P5vnW1GR-%Z4s#t2ZIw6MsUm3`q)SWr+(P(o1ZiW&s}A7mEgd&VF^2tZ?N$fcU0KdSNi4lG4T zHNz%J<8@KwZRH0!Fe-ymsi|z|ispLeND5kmhg|p>`f>V>cWe``N)(#v?B0~p967cW z=IUQ3-#NckYM_rK&D7W8`ktcyMw#f2K@3-SioS+2A-gx3^a{N}oCc>-Q#P!;J}u4m zZK-#X1`2AYrTl>FWs(MAk%5l#1F^J8eJ%!=zP^*vD#%i~fjN@&#YlqDRXaNsDQIi2 z0}la!;!Qry8=T0f3LHQf0Px;(#{J=pW0OjV8O-M0l#wfOwgoSCpK@C&9YgpNplO_`AUp>+*`aEB^#o8j=E0{$9$cOJE*4 zSCl^-ub8J0(Fd<*T}X$NNV-V1Jcp#G^b*m>=X7Jg2$;%JF9*A)=rQ>OkUk zy;QBBAW?svImPgYI#!q=pX!FBHwC{_+#{*{OsT0|Kk>-6vKf`_Og3ufSXn=*!Bqr~GS^@Eg@eOGbjL11 zN%e~IE0*GHuRgAi?{WT$69isO1=(JhikkPo=N_1>T8fG~m_KXvk8A1PGMY%WG@rCb z9J@ZD_C=idAWwW2r(QlWO|$=B@;_Y%dlF8eO!QAjifC*Hgi!C3A1eP_H<>|(oqF`$ zo=`O}Xm1j)da|fyo$TP4toEUb9+mc(;e8YD`-so?r&F}2ytSvjjZEt;O&eUNCta=6 zT#+Xs{~`M@{O#fc09btxSNPyf;e)tE$s{NwpQRiC?p$dT=bHk_xWxxaQ1!Gz_1tCC z4^N$np9PkE#QBS&c!hG6I5Yon760M-@JH$co5lxwvk%2iRh(J18(*sav&{cj!4m*r zB$2*1ksefVb+7>WD_w9d-~b*ZJqe)FgBt&@KI$HV0=4{w{~rec00g03`n+l|QS}r< z&3Z%adPB`yM!H7-SrXG86V(O<%MMV0=15wTZ*qnwJmrOPgad^g1#Bn5K*xsSgUx`_ zfRYb|vSVA%fDl3D$|Ir3ci$h0L{mkKf@0vo3m+i?BA}3@;;Z~u96ZR4n@9v%RI9`eTjnc9C2RRpPp;{uA|*X-YqB%}h0R6w|rkSytAQ=lKm zfmU%0$R*sB4F_ijIPdYvKx-)OputP_(BY>BzO@?TS{fRqsfJ1q*o*YJiAoC%HB^g0f7{2LqOZ?=E7DMd zy9fY2;Sz$st8oU!&Ikqruk>Steq^WUbN$GM>1dQv*@BTukzI@sDmz(U_{TfgkUAJ= zfl)+ZO3YQrp|9~br~n=LRb&BrbD|vZHLfBMg#!RSc0vH=PSwQ0YpO4zd_ z{kGX`D@jm}<3@PTL8MZF$Y)T+ohCgRyfQKJZvv5>SNx z{v`cP{nw+%U>pJo2cxKOG* zWH%rMu1T&+;2a@j0HN24Qj&yFSL+n8q%Zxq#90K+NUod+3UK;H>T9TTfl#9S$cOq| zTraPpRFHrMd(qz#g~o%TLQrk57!ehvr08E~PXS-cFH8dU6l?}jexad0cWDZxG7)=Z zl7eA|0gE9L&%VlMi-voHEtG7A4z3SoOqAK zEGEY*MoELqENpq{F%tnqf)G3cqU-GxI5s4JA_c36 zit>|Nyn>t&1knM&E^ve81^~UwIm*xfz8|3W_MCc7@q!|f`8MH0MI17nriIrMvI_(p zt5m7d;6em6VfcVQ04Q>Xzk2nw>f0Y2wtKb_1zn4{Dpx?@6EKep0M{U_teMT%-x9}= z#FEC5#gl`>p?KvOz}c$^;Nbidu3bIj;QX@`?)>L<;A$y+1zmGX-!$gue^A#I67wcUpl#7>>mkr9p!&%3~z`_XS;^E?evh#5AltI}UnOWFa znHgDFq3m2dpP^7T4(np+5hueH#hqSqMYx z|G*;0f~M9=Q6aJW__CI0dn3u$@RzSUvH0VMR(n6kj;N%thiAtylS{o0Nhxm|tEroG z-IcW*q!viQ1Wt475nNrob~KJ;X>or>)2mn9(+tA``pywIcX|Wnn%aJZsHp;8A;_Ow zvsy2of52B=uWGP%>;GoiHS(=4!9$YX*o(9344bF%u7H00COKK;!Y}V~eD+H1 zL0N0jo*yQco1OPdjK)09+Tw2uJi1($&&VSukDuFCI9VLtLWW+LDj;Ay+B}-cI#Z#@ zBY1pCVtgHCr%==9?ork3mA&E#CV}9ej4mBnbGP^EjOrGvR~F|7_Zug86SYrfPfGXh zJN(#q#9nPVNcV-stSD`-=J${K2F<)`7x!1TvIrHvxSG=@$k&Q$4R#Mm%+Y$y=A&xH z{&id;aOB=|KQDhf=tNU2)x3U-f#l3ld z1F7cn3O(}{CwNbz1v^+SRY_=v^NJ~8VBOCwI}M`4XpgxdqkfEYB7OATAu$K! z2wll)9cl7S$aosWVt?{_@$(7oA*qtmtb~mfxb(DXbZ74n5OcJ=A~Bhsm4~Z%H4Sb- zEG%+JjS?{@$PQcl|Gk^6;cv?kHknbLhP9yXs0bp`acCk#T`=G54sb zvWtA1)InHR*vXw^OXA3_vjNgYqppAl*A!jvJYN6Ma{2+s*}v_0YHi>9&i>}vtamBrPqFdg*CQ|H`~q^ z=!vkuM!#___OVleWBPuNW8-F~`DTt5LaZ-Rj+-(x%tg6HH_;{P*$B;_U9|Jf;*uwl zl1diyni+OapFXwG(}2a?%weS@vQe-3;C3UzN7iL1zK&bdSGebEwpvYb#y9MxLb+cRL`EBY%XkQd08sQbLgi}&HG0l*~R>6MyAU#$vM3~ z26?#cp~Ml3y~!N9HaW5Z!p=D{I=SAm0i$5e-dP%PLf4yirFvqTIK=^}%2j9D1 z*u8y~>=#w}sjp8t|LQT|eJ}`{li10{T1XPT_r>RA zU%Rpl$I<6e>dL__fKUbtnY6kVC}foF*otNoy6eORh_ zj0Bd*Cuyz%KX|XFZF}ylCqU5wsHd18GPGnX`_`=o-!UnpOKeFVs^db$twnD20+Bnl z)#w@f(LjHoJ`<8lPAL&gb2l?VU`N(>XN-uPO#+DABmdBK8tt?NkqW21(`n| zA5>M)HCJKyJd60AlpD!3BzW(HBWx#lM`i&s?nL1KiuFZyJm-Oo4 z>%n{QowKe(OD znJJEtt)aa*rr)1AUWza4Hsw8=l3pA7Q3x2W3C>P^8N@PvkGRSH$>x)Xl1ip=>QG15 z+6nw@9^Ke8#DPr|55*ieG*D|TwA<88RGfsu-M8+4$tg}Z$4)VroT3~P`!RLc7N+pu-b{%S}67sg&YgBZVl?#3%LPt zIpA0f$TBgwU}RL(zQ$<4@=n0gQ15pWCV6h5aqK8P@5?hJY+nbzmIN7zhW(<_5v? zrd@TKw=W~{3CaM&y=`-o?kVd49Y6`3l;cTw*UX9?_3{pjI%e|Js9g@$6L&Jzm_iwW za1h)?aE%pzKa)y~wtGU3&Th}CHY`hE3d;{JyFIjCWPc+*;Vr*u8Ma<&@-#O}8PD!u zdrKk9j|m&_H?jf;gfY@*k40a@bfis?9E4`K47R|m33ru%`tIL0P_yAG+=*zjm*10uAyLznKAo@AAk~mjv{YIlnb^DUQw8uH6nh{oiHe2PgQ+wdH<8=_A`0a7! z{TIk(7On3k_zzd6)&}Y&+B6Zk0>#zNGe{_(-CjS&`@jjl^PCyqgQ~Nq^!oX3*}ArJ zR1o9NLWq4#{!bytV-9MqWnu5Se?WFcC71Z4e-NlbPC!InhRw6jF ztE$`gS4W%*Q#tRcL^z9|__O-7wl@g}9n%07sM>pF$KBG5jM+^%7Rm4LkC~`wjAHy` zJv-n(VmmGlg8zKQxbs-q?X7g)v3TbY- zK~3f|@*YQ+5BXiYS)(VEZDoQ@qzJc3BYs<8YQR=O zSMS_7#OWaCAm=y2xBYhT;dTLeEMLV6+w|GMlf#w9ksp--6K+l8{A@p!y2JBl9%F=78-;Cg+_9Vx8)8a6+{b-Bd`q!}fBr zMl?kKRf8Yjjn^`Q^E8RCZV*H54mPx14&vV(?*dLH;WG5*fg-Glxw)5Zm>?58Hm3d_ zsMQU-gBB_HMQ%7~8+}C-`S2-QGxTek)DJ>6;_9Qkg7!;uRAT_UgY|dtm5Jh=E5A51 z2yB%f)1JQav5zKG<1x2zIa)t%oG8y4zNQ7LKJbtY_qQeGouZg=DV3?b`%-|*(Jhkz z%a9Dh_GoaOAb3sk_pFz5-Q?SywLImlA~f#}t%GGg7x8%cku}oYo1n|8{Pw~9W=R6|;DgfoLYocaN&GlQct)^fkF-9I*gj0{E=d#gb^Z`6=s zjPI^m@m`hSQO-DG<3Qc`ixk3_7O>d)`OL4IO&~k^iU4=GpR*UIZK?xsbx;tBN$o>_PB=2oK28|AS#Q6KXM+3QKO(A|GI4Y>Rpj?kR zS!zZ_dt8N32`<~xv1r(K-rA|W3e{8Z8W8UUSM>eQ52Y9J<`YS z`tjDZN0(g2v&)keqyi(yg*_|p@X#wwiRNm;(A^$9;b&Et{WB?s%06NdIhw75Mt5X; zNDzFX=_^yT8xG|_Xlp}B1<*};QG0@NMm0TOVE&f=m^^wQte&Z5astcoJ?Y(v!x@VV zjqD>iX|ySPGl*Bdq3DwcT5K7!;jji{Y)#OrW(vOXmiEB7x~#0_dgh*^#B>|}8uE)T zN+x8Kfra9sJdO}UzfBlj`EQH)o7OY%i}7;4IF*OCtcYRlVU!4+@^#UH=mBuy+KHtHZsGQ80b<$bY&{H>U27#@^yWy94tduwa zWsN!C+KEu;BAZ9%$+^PalS{<&{LL zLY?0d+1#2=I+bo78=N|@kUf1jxUn!K*1GDk|7&3h_4t@hx+n0oMkjQ#_b1AK^vQiROMAW-;1bzWo#{`YHIgdRS?%czFh*2vbk_tfM@2`YBb#szIicM<}s39HZdeWd63 zWlw+O3oh9VCpNRm%@x$3QLVq+r`C`mVJi|B&lUx;1r2WVnjAkA-jdx~_!Be!hPD0IsY9c<4Vsa*z_g-h0Q`; zt>y{;IR3PVique3I{gvMgp>LNFy z&+fa`Jd5=Wo!UG6V#SA+zTto3c{VX+b>Cq9`pq>QC0;$2Nv&;%QSIB`c`yS{cXZ8< z-76)eKJlq%#EN}EDCklYjb)G)!0OyGp5-mfxzKK{%$?Zk|?f+k>0yE|d+*&CPL_gM%I19O33`WxvbK&C>?u=H&rFT-+_Jce!}Dc?1OR zvOzg`sfIg;V4)rk)Qp`nzPM(HbGnT;F`uonbsVcA%V4v|;|1ja$uL7oaNoT}B<>ib z)d1JR&;F-PjkSrFQgwDpcyQF5z#Jtv^q2h4XS2nVxL&}{=kSJE4lU}~@soHn3Adue z`Lour+{v8{9A1;}HhF?Hr*fSU^)q;q9$Vjur5P&SC0_O&Vrh_rC&ftWjtiIq8Z=i_ zfGBb|^VcK(0P^{-uW386)w^r;n#zqiR44uJ*ig?d(hmP`$*>c6BN_F6*4&(%U(mp0 z%NDS^@}UH7TR4SiV%R-@@#>ZQ3z*ydg1O=b7W*41JYhH^DkCEUPocVvoV{4HJ2O16 z4wP+2=UT|maK$pX$<7UYJHFTE(xsbSH#uxzjnv43A`HK(DDw#W-!rkHw0Wl!n%BPj z=Xl3$v88Kp>jJS*8;jJJJZ(nM1)tB@jrn%l%6i@>^lXvOQ<33@SN=kHkMA%ZYB^T; zMUJjKeiC`hK;fgnqZ?bgA1w7~o)1-=+LZ#(g?QYM;BuZSLLdTg!p+Y8P+_R!y2Z2O zIK?$9FD|D{Q3BybZp-Yf&YASQi~weUWtOgKVV)yv4CeTJDeXy6zqtL|8{AazWvQ8h zppGw=^AW&Z$X91>^gH81G?NwKFK5c)gRpiP&kkjA9IA=8cd3Sz?JegCOGqDjx+{K) zk~Zq_(lmdKIBPE!U6S~X9ma6PmTZ5yMWx$TJ}*_=7)_|qB6CXJG?kL{K*?N4X9F36 zMY)$WTq4jR2T{j2@#u|)HLbLsu?8j3Unq3uoVckKh=i&mJ6?K>kDGob4?%eEV|&}8 zqs)QPKJESnl=FGlO?n4RTu%Tgtc?X{_L-j2bP73UxFxt|dlEBeD;KmN2(!BEu?}jZ z-_B8JV3Ms&Dk~`QPM2OeWUMJIQ6EcpXOhG8bj&u#xes@={0dnL#;%=lhvYmk^XtW} z));#yYSZRM2Uno`lNi%GHMDwMkO+xn4H9H;fTc=_wQL8b7mkJ4XLB3K1k+abRaX0_ zGwjLBNJ(&tiKdWu34Tny&Zg!@Zx64cCA_rZ9aS{_PBU?x7b))D1LPfnq~`wJLVYS4M~g&Og)*EIgTPsF^MWypPIaL5`N#exGhO$(>i zMSHE{9(*mhBk6Rx+-bsNRe*R;w_m#!jND1E-M6%f?n;`p+0?lb$h zExLj1wGf1d>VlTZ)Q<0}<}sEd^|P!l95K|he14*Y`7jl5`SJ4n%W0f@yX?C*YXyd| z%OjI^^y*1D@mBs(n7q~2Y5#^39qX^MIR0#UX`!d_ErVg?nT}%@)hjKZxGr6Pw!?C% ze$GWKGtNHESzV&GE8P+2V?j4(H5Bkn|5)5R2wwR!Ux3-(tvS4h_T*f*TPy4-DO=UA*gxFegt$ z3p8MF-*G|_>({DRjdepT0bCs5A>DkOtV}Ixa)M4F2fglt^ukGYx<-On&BvblVkwX> zAxgj$-je!l#``Hf9S3j6bZcn}ZgJv65gP)9tHvf5{D5S51KzhL5l5UGmF5_4smse} zQo|{RwB1EedW5?krB-}UEu+nKV_=R4604{B@JMp7dy)3U28FGR%XQ0~`M9V8Q0YSS z^32&w_OhT;t5DM?@2zhb=M`q!3{W$hOObjU-%K<^T**EOPb=t8WYY^9Rhpx06B*6&s^e)dY9r-|>3cnl@mx%2d2zT`c+Gn26U z{J8r0fckYb3B_TQx@ewPu;yS2saxYqi;s<+NWE&w^$+_sFlTnq^X z=*ME;F@9LwKju39ip9OymlZ)J_;2X9W<413Uz|Ll@PJr~S~s?ez~$AYG;WZ* z=VKT32@^!+m|BbXWh==SXHkqk0f{!mWp})B`&)#9_j_*@lazjgqOV4pX70j5G4I8u zP1ZkBE)C@nAY}X)UlWthPebAfT&%3MpH7U1p?+*KR_p7paILrjOCuS&@O#fpHdrz{ zcSuCd^Umg1V?GGW?7eG2yAHh;3O@U@)wtN&aDan19ql_QkCW@)s&FUdl@k8H@&zy{+zTJ2RhdM23>TJ3dUlg1dNtJo^TN)i|yzq%YxmV%R1k8 z%R|xfm>UqPi5B}%=ZXC+1^3VZAu~Sm_pyUi=LOZ9=MBRW9E+|*gB0*o{LRs%Q|!3q z8>BJW9N%Yf33TkdQw`r*95#_M;^xZZ;)*&8*zu$OK0q0dkcp>qrK?>D*X7Nf8r< z#hBnx4SyZG?@c5}-5Jr*1W|WJ*pS42yF7V(B>j5tWT!)7T#ioMMg=;RHreAn|0Zhz za={_uVRfwZ>*p^Uwvh1$sN>3>-|@dnB8b5axKHZd%(aBOfYoMab$NREUdnnKMqhGl z9cgezZ6FU5A|}zL8-4pU+L8R{*I|kI{qL7U`{z2+v0*`>gU~%V+@W?y*5?HKi}MJV z%HSBYI8(cc4?E?c$cOO;Qh&eV+GNY=_lyI;>{=O!D(|!|J(b zsrmUWjNB`i+)aV*_{+?Vw}>Etww+HRh!nDk$v$8k6?>RCwW-Yd=l$t3MwrcFD zs|??66#YJseO7rdWSz^h;?pAsbEg<5e3B{o8BKAu$&7`s9VHMtTcrXc_A!BI!mpb3 zd4YF0CihHPhybc7MGr4hGS8>}K-kp4`-JXoPffbOsXUePj8WmI0!(LGGFUBsstK*! zBHH4u=P#))rf~Ek0nME%gW%`JFH*70ZS$*pEffNB_;yD;6;bM0(i;5J3eu3>3up;`H$ z-$dYL;AFygyDqCh_{FztW8=mU%Dy9X!O!g@^2XZ~r%QbsYBiV{>qcojyx@{5>a$tD zBP0LIqq86Rm)OVypHE?XZHSN%CX$K{q{pb#*+yK?ef%(d8)l&qbIkP7S^3UU_i*5$ zR7&BFg68NjW!soQV30A5gqm4?im|+;Xx|%aT%8|51HL0WpQOVE?JHfduulgY6|l8= zTlm?^xpjQc(9F!_Te3X&dp}pa=uxKD_FcVnGZhofd>g-~JKyAKGkfBGi%wl$dB|OB zfjcm|KOET5j%GAUH$lpVM@U&&b2wAWSnkh>K3o}kQeNNp3RoIPhW2Xloh;KQ1OU|x zM3}gUSPV(E)--+bcA?HLHVkg{bUiBTxb-fHZafsDMd#1Y?73U*-g((fzTWbJKgcWf zHUC$B3$5VtH-zP_)raEKn~lhg=FrxC zEgSmN9vzvwd>l~({hA#-X-0bw2sIiDPoDaCO~Xnmc#~@)sOhT!@+Y4U60?XS3SY{w zhwsmTQJ2CwiMaN~`-lg_9;qQk_DgWGqJ-qpMrH_fIw4F=%wZ947YOyl>64A)lq(DM zrjaMHnm8xPS}$qTfRN}ycJrm8{;<$r?8!$!p5&JCeOGt-#)PdR4Z}XIOh)g_k-`lc z`*ElY=tmL+ z&m(l)%uc3%Pm&WnhlDk5m^#;+9bx#xauv?Ie_}?a#P>c~Nv51*PsfZxC8?dWkES-) zF12;P{Tw1j4`LUl;nqFtx;n!q+SZf7L$2wx?!UgzWfpEcauXAsc~tX=ux1-M=J?$O zd7@P!XF%qA@6k~eP2TJIGZnvBv1zwP>mvO+bQw%@%*bh<|F{R$-r>%-+&o~Svha&T zKFZ;ild1mDm~pG<{dmIVKLM63g((o|#L~(i+LU81#tXFAhVFgiSIM(cDW<~PdBN2u~%dPB6VunX*iZSZMzQF z94&pY-R|df2JJ6t|V{`AebT z$!;jqQ(U>}?FYemBA78oI%l?`$HGw(YHHBX?@Jq&bSqlfVN}x$MM^n&T6*?#sl(@+Bxn`eJ#USe19OPzAF`* zTEVOSBsgJrbxyR9=KAdTenCrVDylpya%T$7pNNeEh9m%c@p-oO^MElzk3pYHzlsVGCIfw&x!DHfyXe| z+1WXHzkKE8;bvoHV})|^@ZII$=9~wQ2T}~k{L5BzB+{)9f744 zS=Bks77F2RmcnZK2-EIlmTY({)L8~~nf4Nk8j0_-l->LJlF-_{Gu92CufK}gl)-B2 zWvul?lm428eh`fzy*3$QQ5|pVInxD`ErH(&(o!cU<28@x^@r$b|DBbp!IJhow=EwX zfEO{+)+>IPodtPLbrD1GaW0wZo+13;JbNXSrH=t#E#@0|R|bPE-}=n}h{9cZYe~0` zxyW!%6-a+$?cJw8Wm=3FPzd)w6`s3@@1ALVf-8#{n1#Y6t-%e&4rvOH)%2egPQ~wy zMq>9fcfPtj;Y?-RJTlgnp0Icz)nM5_Ke)!2~aTW5z)y3gq^ zkj)TurZsY@2rPsB89A+&+LEz%m|3&wnxh^x+ zJi6be=2SYgF|Mek>(`z?;d?_2b}etvzQczgn(6KL`H28%^MNZaIeP7Q?b$e*~H6boSY-N#Zck=ns=>JN zHkek-)%pTOuql&@I=bTyWvzd!V4abMh8|;lDiJ4$4)o;?=?$onLN^B;bgxsH<|2MN zT$vH=r3Ym_`=P=XkT))T8PG3i-xrtD(u^^_!E36pjH0viH1MFkkO(2Tl^zzWdSCEa zNzL7FrHwU0VU~npVItO>@0wYmzrs78nXAFOBHT~#&MIK=bn_4TS~nLqj%(ulOydb$ zjUb}a7&<={Tq#DzP7Z21tw$~L=AyzWLA`WM&niu0y+gA|L=jOP$>q;d&?MyZB0e6n z&aOGH4pkTA-lb+vX3kzds=R$m)*6dWuS7aabmN{z6Q5eGT5r@Q<_EfH@(r5ZOQIQ8 zFNKB<-+vW>odV>j2XSiyRJPTvTMYb*4N0vzzfJpz z`FU+k?KgulNkYccEUqoEiSdZ=Z-~&!_`qM6h(mAUY&M3C^qL1HWWiE^EPV8kV=8Ti ze;4;O9~U*}Ir&QU!}^32k`MHL)naCQ*`=;@-|;VWh({xy5#A=hcpQeWW7e)!BysQT zLOTPmqZBQTxkTvRy%Z12FC?PU6JOSgMID3oGB3RH+#B)qs5n_#J;^Pqe<+WPL#88C zCil>XceJ;1W2QJW^dS+6q1T`nV^n*nAQ5ix%f)#El)J>U%i(mS5q|tSEPPVK@-nR7 z>LTkPdEuhxXdiRzzCG2pa#$ODqfKAmIje}gBYH;{|6G2{*Y^pUXVAH^_0RbDG#~1f z3_|3~9dYmH>i6%asox8h1RNF-CDRr$>=M0~TjFsCx_tM?UK@BtF0MvxpC|>IAA_IWfA+ z9Pyty%ZQ}aywoLKu72SKBqCtlr>#T$w@26OBtp@t_TQR~*cy5KtQXkfwg`iGA00JO z4fZnOn{MImmnTCUn@v-L@qVbMnxN)&YK)Oao|5n?;Z*-3Aqe`U^Oid>^}%<;>#Xc= zH~Rff+^T~uv#=tF9Na)2M)Hn(pOk1w>K8p@iASfIsYu#8E;fhaw_7)IUQSO-d6r!b ztNbsSHVrrWEUqE3##XB>EP`nu*CiWU0cYf0ik{lwS=o40NiC@rlDhe)?xoQyJ(V(2 zNB)QTR-!`&Kc3V!yBI&aVbq*#^EG#vOfRj@@P+N`QWcaA;aE`PF`|QTal}1u_V6f` z*cEozjOh8=U@H?8A8-4;L?CM;dtswSyUn%W-uF*)N2c4wXg+E*1Ap2jM`b3(??YCO zW(fY%cz}_|N}%YEJsZ0}=9%S6Y-TOLQsD-pkW|7HWE@p2+?kSVf zA2W)jibB4ho|4*)X`wm_)IrU@$24{M7^x=tCuXXor4gIEKLfnut?7FtRAm_80&!cL zhoN;CO#6nr)0{RV#~kVx1ONF9@6)$$hIeTgF-KzMhZ;ZkfrehWIPutqdk>U&o$~FI zWta|90-|g)A-Kpj|L4V)F_SVoySs!Bu_X^4tl2f3{lV*}?bRf1^G8}%A6ZYK>MolQ z9l5X+?#9r@uyB&JDc7_{2SYZl*0FW6wC%=sf0%6N&X#(8-X-TWgWqBMLrB3ZG1BV< zzP{6IpJ8x$Z zvYT;BCv;eh00XUS$5M&gE+#(Z%nvL5+UydOrNv7!_Gr^?o#|uVa}J!aEn?4h!DE!_8H5k9K%Ic>B-Be$~Jgi_X65)H5W|o(Pvz2#C1hSr(qn z9S&4*# zadZJehO@M{^JA=_p2Eb=41H!}d1NuK#=}P;bG>cyfify+&pS}&`J|S%hNli&1nkVG zttC~s$HQ0y3p5|PgywC!W=4zo9QtsnXP6$_Vwx9v>@L$+H58U=Ay2~|r(E$2mOsW5 zHQPcOIJo#$E@dL1+x_dawu8Z`*|)PT^9KQETkv1%?IGpEkOu~SlV_kFHoKGcR2($!;8M(Q(v87l6kKmg#kUSIoD~JqBKU*^PaP-ZGEFlK`yx z+7LkyR~$voli6c}8B$=b46o>Z&ZUZ4ricECZ($jtc<}`J>r@~q-MXh|hyU!)S-NYJ z2bZZUvT-XgKw&2>pq|7SU7@?_X4{QEjyg1_Geb_T$zsiw5IJ*bfz7%dH?qa(ER&0U zI&eAQT zQ_t|_Xd>u2*bCUF7 z+g-zqjU~+zudl1ZZs6nVOU&>PTz1IHq2a9=lKZhq6Mr5lTH2_8EhKG^mvC-n97@f9 z3Z9H&DLQi{RGrQo*Qv(7gCB;)u%^saeWl%KGrmJw-5<*?qMr5L@z+4Cnu7TK8u`HV z#G;Ua^CiiP)4Lnzz~xU&d!=R*ZD5NlRDCHlx4*RUgNX}q z)0erUsyx5!DqE9vZuU2?B8fT&v|wP;AKkpfAm37R9FQ}~2cPb4?k)DjGj(9L{jmE2 zEP)%pwkt7b`0?@q`8>~Vyc_1(gblna5of*@bQyfAWlczn$&)x2xS-q1lBj*Ml3J*9 z!ItqTf}-9yLtsh49OwRJ8R(o9F=-~(P==vx30*(%ODCr2PaAxCFuEjXM>XE|$l=)LvsHcUX4)@3 zU%3r~2OoBCxa47n7hAJL|1`$$l?P$pqM`=##p)-@jTsJxm#jr`6?0|~^WV3qkBhMC zr%e5W(WOgWD+xEuCI-Mm175V^_43%)+LVRu0VwQb`_6x^v7*ra*zYpKY@% zqzmHj<}H`3+{dCk=(KKH?cfTQy}@D~+XJ{n8M2=m*|w_52JW=$yy}id#26MNgx5&e`mQapw^Vaj#I?Ng1Rr;+(v)7Cg*wh5~o_m_jIos~a6#0omyxURw* z3XZgvyP;A|?0f8us&oJEOW|=y6GY596Wd? o-Cx@&~5&=o*p$O8O5D-KHC}4^7-V73Y4M-8`QXT|R zdX-+J2m%6DEC`649emz%-t*_Y*LS_=`*-G=?5vqtd#ye9%&fU*3|(DK02<)$~ZG@0p9-dG0@h4GshA?VX^@i;u6QzBJ@>0-ouIh9XmIl-(j z&nL(pF{pZhP_}fO9^xel7mbGUnZzPGNH{E-QOuWJc~!t99xIM&$Rw22J$&dCQ{tP9 zT@!WIL+c>IlCg{w69cr4!ovw1LXd#Nxuf2JF46VyP`2RVL^JAlwrRlltC;~K0Dwj3 z@kY~xhL_U-@&G`WD;~cj9xtMl?1bStQ~~jZ0YIBKHztX@r%F_JL{fKD)7b3JxT2Za z4?mkpYK9}7k5a8_=oF_>_~GrW&b&}IOL_nfLZmI;4x{}CJbg(a%m3X z1RJSKL8XVFdCB*`au-465=j#S=32eJF+IbhX5%R~Rue9BKRg|d`Oj_pEo}QOPlh>I zK6LqS`A^ruo)BSZ=%0>Ic%Ay?)^Z;hq5siMIOwp=SfJyE=p`S~o@7bP1Eg+^%D||K z{x?lyZv9bHt;@bzbG2I2B>hQW{b^q_%Q_p&de7+zPrHYn3)^@8DfO>dn#4n#u7KP*ql>q=BbvB7d7o3k@)=3t{JS@ayuUO_^buV%aEPX_CsG>ntp(gS4 zB}CB@B7aFKUj&=)awWgWy+S;rdb6qGUuEgT37!Z5!$|@?Ndn*m$AALl5M6LB&;Z`7 zU5VhN2WR}>eKh4 zLqrpyUy!1bPodO-fOHci8`1(H^|A)Z; zi~tdgi~u9}W62v%JPOgs1c=XXNU{Kc0A@rI0K7$YN5R-Qf;6TB0C0!o_@Ax)>sAfW z8j%*zATqh+4kxAn8r%R8C8xq>Zwd6#xbbU@0@;k8Rfyo~0M|V|I~WZW-LR7EMsD3$ z0L~1w<%)v=KARB?GRC1r_;hjfDAJHS51)Q28m2XLR{vK1TG}Z+6K;L3$b39A7)8AS z09uJMalAnUgBuMX1OXT>RY@9tRT<*a8Z|X@OJ9r17mqH&mr5W9z8(g9q}kE*)c|T` zNogsw{#O-aZmOnP7*#VJ)GXa>|A*$dpHOtZTRK&9vq=NoDUe`zv^fU)PA@8Ls*t2L zMuNMC*<^}~eR?UFHk+zhjr2hsu!dOL1eQS^Q!|ikq`2@cf}y>)`r0gDJ?ZB%T@OZ2 zf%71B;YFS1Gvje6U^;e!+6Owo7#uP=Jwy@7K@Tl1gp9^O5utimY)OhKN=J->lYo{K znqsvn;C|Z+C*g1sM?+1q5)=Taq-6wu)Nux3KLL_~NL;L3XC?`U=*%P-U`x22K&C=* zj+I->e1MbhBohWPAkP9>L?J1bN;z=YLs9{5?&i=e@HGSlR5=X*kJ=yryiGf4 zfJr-;naz?3D5L>+1-hz108|XQ6)+>nArMNWzJ4_bG+3kmU@ah9JUSmlp4xDTam5*M zB&a|+TXJi?0;3ErJxp9?d6?bk+i3;Mc=NDZvI< zu{}|DWb;_L`Kvhr9dHfA8b`}P zOFGPjUXlr%S3Dl0BjwgaYe|knWC9F}m#WW}>w=S#uf(*F1~Hh&b-Y0mf(6MqUAZ6g zx(G4Kl;iuB;zV@3ROeLCkQ{}_s;p|;)(KlR&g=B4Wt-QjtZHID=#yN%xko%bV6S){ z&=8%fRBp$-vaknv2!sf-sCHf|VWchq18fg3VGsos3WZ@2d|(|_LLM>xr+g@hQ@|C8$O?gh%QqB<#UQ{|XkMrh4uJ@x zvQ*FjR*Z5e!LT}%LU7tr9YHB15{_An1iqG6m<-NS@H3!!g{C-(5)vmGDi)fI#UfHn zu~egAl)X=lvK+q2#x5L+>E3T3;9UR1$@mxrNe_<`6Me?6Lh(OtZSV~)CcA>2VjjVWUwErLSQx!Gj1G z!svlp0H7gGjEuZm(Y{0@a?vTI;KeenCbbc$1QuxlfC(ZjoZiSB!xGCH#}?0?a0DC< z4eG`Ku3im*hUO1lqn2rC{v3td?5T|aKs^dmTlYVIe=8;?9bBTWybU-ae1`4 zhC21m6*F(l5x!Z>S=oLWBs26~8!s33g~#C=VZ4(-DYN%=3({m2S_*Nj`O&Ovpzz3% z0#hdkpE)a8&n&ORon;scgdsMvyU&grhVnjI+pk(Xt<2>;^Wa6kj5P#`S_DQ`V=wBO z>~+*a5$wuR+0=x6>}@JmP2NBtFqOSLnM`Ay1Ylu--J9us`?&)DE^Btv5(0 zd!h$-6Z(m9D4#aZ)&3HiPIB!M8&wg@x>73sVd;!4Fs8lQQmX;OZ|FKSyg5(U%9%G>j3+SfAqZv)ipH7BJ$VyA=Rlj{NvU z=-#)W2VO3PXDkN|ujJi-WehCIBMmc#f{M6In}I}>5|DW!F}B$!b}HR9Y52=RQ@rcv z@J#5pos)ZPfQn(liDPCr$@N^s&%jWEGO*}LTi)50cJg4ntgMU^SrKSu`?|SA{GDJB zPZ|xctIFjw;`e;#KDlHPbLl&1frH?1du+9RZ^PM6ioV*dtroNFyAC~Gr6o=?`U&?( z92l%8O_L`5uBohg?j`sMZqlGothN{GJ<{Lnc&Wt~N`HyyH<|r-(>-T%zigz_Kj-L$y!HF|Ex91$$^;*u&`Ksg4+9t zvn{ooit5#MGN-Zy2IQZn?u|_tv`5RI({(-x2RXp{2U(%kdozOd3&@FK9 zvM3Clo%`C@TBf(3CdUnZq{UT{->yL1y?YV3+e_)-Ya`x{alLX!CnmUd+s$C5dX+Vi zVFw>SZjwxCxiqx?w#dke&p<@O0|3Y~QH?u{C+xp0^_+DYONx&g3uV%k8k%mp-7J<= zs6mQV;U^AP3g^D&$R}R)pplP8ji{b(L0M{n2hlHF{AV@Q;?IAiAT`5-w1<;9`65^e zE`+DhaHGhG7y2{1x?Aj$k|-}*Bs1`v+@U|QnMKYT<*`c{v;PE9?oS?ho6m7madu@` zm*vL^xQst0LjH`Mk@Qdunmdq20|rz^j}*bpCJ0!5{tIV!&6GdC{WOphkKB7dKeH?E zrQWnQs={t{w)5fJcf`u;P8}mA!bUM~hR-gd@CiNZ>cdD!qg`hM-|(jB%AFjSVwPDU zTA-ALrm~2M=d72fa7V!_S@Wykul7E(M<_A4&$qKhFbr8m@zPTfY?0uTSfNNA(ZaX? zn(C0WEHe+r{+8WUsKlkq<`8Bmy)lE?04G3n2vl4DY^^Kva&hyfO27XP-PeSl6PKUt z7O4Mn?CJbjd~Cor{$tb?LqQMMa~l5&}q-I`i#cbJ@j1YYMA_6i` zP7%Jl!NPz&IWg_esc=+9{- zdV+deQdwzyx}83!3-KxmR!$xfRurF$1l04s4wCx8v_Q>YqIz+rvxPbB=*u@|+Y*Hp z$r~}_TJ+H|I=9Q$eU{3LQorK5vZUWu({G>kPWi64;Hh`y*mH3N3?9aDeMXH<@|(<4 zT^w+ioYL+2ReX;Un<5(WA?`NWrR{ptaYoOhzLl!MfpYeyf8lPQGNBO%uRDuLE9K{^ zh3lNu`dSO|zwvSj#tA1=yT;Mg0&`rxM-9&llz7LQ{a&_Ko0xRQb7JBm#Tp*OI4VIZ zIRV%JHzfvu`E~D;RYwwPaRKq;w*pyzypwOCr1Dz4++tRp)-6uewBI?Xf&35p^F!Nj2KgjZ`v)CJ+lGB{cYP$u0}(Z0ukU|d zajC^9D%;xn8`~;Yr#ZdV8SNfCe>*@HzzUNQN9pin>)R#mh$3caG6dDST6N}y8+kC) z7ZKriUD`)dHz}ZJK~%j!@5Xo)D-VLTuSqTKozCJI(-x5ZXuJ*@N=pw7jD9)oed?-< z6paG|8>uqcSYG88ho}{$e^t+0m>5h4*n4m~CBoq@<;&Vhsj=rgIFkfjw|a^bQ;<=S z$^A!ku6sqBKlP^lGI*JXlu<-OzKp`y|!u{vX)% zNS%xElG_7m?}LqwN;-;vedElx8IZVPpL9|B+v?2}waF!wAU*O7JBlmoonnYk1dNX< zNBDJ$l(Dczez@aGlB)P4VJ4mI2C0^V(c??)-o~QsMGuZWb>SC7CJ=6nyue1G$p%|X z>dZGGM%c8rOjR0g@bYIyz_!0tJ?M%%{R3}!`bx>yJOZqJzbZR>;o4>++3%OzL=|_?+Ew$rf1tC=`{_bl3NiTuN4G^q<3^uqg=;H&GcG-Wn3 zWtMh8(^*4L+_=Pj?qH(9p237sU+GBvSA}bRsy~}tJ{jz(W%y#3Be+HN#VEwjWu2vy zZEx7YkZ+TNQ5V5(YPJdlkGTbQ#m#=V{C3u{)45re_2JP|Q7 ztBH%d?%O|i>Ym2G`ML-@Yu{RS0?RZR+zSI{q=SPL>^G$<0&To56-*RwHQ+QYd0E%bo6^%9 zkbvKhY_e_O$i(5O<(rH(8wKmyaWm_$zMp$pXkgHK0XnUKR3(lkTA7OkQE3LC(-axx zrM($+ZW)&GYF^YSWY)i-X$9}McIyOVAt8VMa^e^Z1CSQsa14Mqm#lDJE}QU9y;tNq zBBtF`Bs9PHdiY7&IfTrr(qw_qjLg~?p_NiS$W)>Ek_T&DDWQF!1I#yoA7u9eYxVYX zh7e);8xS+QGyh$;FxK{4hEOxzC|kM36T7e+fSCp$3Mo!M-Hhc4_gQmo3c6eDTAU!b z(%0;F=Bwr?-ZR5DdyRj()$h7FpQww89>a@VthAa<@)gIg&FlkX0ue`eio+76hP*y{ z%Q;?Dt$G8KRu_p)uf0N`Bsm7nNzkY0@+WbND00E7cFnDU`EDD#f@j*Cq2CE_v+rj>+^TG#_W_kP!- z=zuT8k2A9$$75u3zD#6IC&LG?r2Y1KxL~r$5V#y7hztLK{;5S@|2#culz;Y%Z}{hE z*qJaoOf|POoxrw|0%X2hHK*V%4K3-LgQP-+#qq{j*Mlv_YHr)k5%5;F?GY&*;e3fs zTn9Qf>rhb@zf+E{*m=AYen)Dk@3Cct{|7JAo0kVI4V-f^ryde~$ks~u&bSO(Jd&Si|;mm@iWIG0uV5%)ea0FN~_1mE{KS+pb|BsVuU0cZar-c47^bS z99}Ze0p8%v0+^_m4B#yVpfW#bPZxfhczeG4&iuwr$zq$qePOBeRqczdtJ+NsYLszD?p~`+AkPvr{$qAYuA`(z;rP414|Br|Pxc zgy|bU_^M(nR_7Ldh5cQ_9zV3}@AQ}7=V$XPseNZ0ZI`21T%yg3gR%?w=~n|cd~uI1@|nEe2lxQZ3KO@kbGwR-T8KCFCON6TMqqh9LJSY za9u=Ezvr63cP(eDqi8#8Zm0y5^O#7{ad1BG-@)7~G?yQgDM>B*BD}qaJBT-zUA>-6 zpsZ`Z7LRyQa|sQ`?PGY)&6j<9_$jO14dS-hUSdWlM%@gV7Ebu+*W&-zm`|ACI6byt z*;vcrm{)9+xiIiH&Hb8Z*Lj)Cstkli-Upw5i~SatacmRlnW8tX(hSuKW;S0a1MYV3 z`_(3p(7SV9tw|Aj1s6YVPpjjgE9Zu`E;yO6ZjQThFhSq|u!}8uf73kG%m>$jll&Qo z;6d^&zMXtF^nR`=f<5cP>laA`?-{-v_6DVh*1@0Y%i&LyP8(jmWBzORgH^JP zf|TL7i*KNhwq5XU!X97uP4nUjbb_vqj*-r49V0{IKVSVc#BvPc>;k?E`8k%UlKYr0y9oUulx z2X+g+zCYFfglV-BM=G7Uk=(>%#?deej`8GX9+N>6Qwxzc9#wKoWROg+WU$UvrKEVA zC<);*##B8w(yYkGxePJBqb&zb28)+i=I5Vcz9jIRTIl4= z1VoOe5;m5GSuli;_voOE<(#&s{ca`!D!k#K)EuO08LWDft3^MpXMM@!cCU%el$it0 zrq{`QbBOs+rTHMwW+237FvQulYMX0yz+ivCj+B7mnTS8sP~~|OtpFIeN-VI7E3ir& zG32VEa*5{vP(XGQDK6hIam0$NN>3@EXFYe#^Y$y;9VRG7&$Cbw7Nn5UpOOoolJlM# zqw-BlGZ~*o*U$%N2tP4#*@Kwx-Re|+K`!BtqmMk(lo%ACJBZ_QZ*w8zm--}4YXlUTW(2}&I!gosahne&4mgC<`8dLv4mkxA z4Zm~tg%acPe9{rliK|q2Qs}|(xJ@4FPA?Q(y`UxJpa)M6=YeFeo#_HtCazXU+S@=L z*uxfFg>A^=UK|}m=4@mzN|K#Stu}rfy3rtCJ4%ESNmpEq26&~`3J8FS9ZsL# zqt^wY)U&avfI!0@o6}>5vPf}x5P4)^9^(tk;YuJtdAj1v??5a=cgaH! zcuWkLkClT*+hdr5?4&7>^gPW07JfZJm4D2Zp_Yr}DD#QRUwWMWucUDQgq!|1e9_-y z*Z*3IIOt9Y1l)YEW>w2 zbrz+Sz~j1|xW?$1D zaTCw#G{q1(SO!@E_8}CFtOor?jL5O4k@%h+M(|F8P|!!jl;P+iK95ToL+&Y%Vj9m= zBtG;=a%LnJx-W;xpp)S!CYNK+B$JOLSt=C3P~*>2ScVn90B&1E5r)s>F~!w*a9VBw z7w%K|8JOGxC#GfzPmYFB<8T=aaH)W7LlPE0($y{11zTmFAb#5f?a92(8U6>XTFaV>2 z5*EdX*J07_l7Na;S!G7swOC7s6e${g2)qTdL|LG!PU_Lt&S?koAwdI2vB$6+!V)oi zDq7n1%9GgDCQn2)#&OBU@FD_3m4RV<7+* zp;T1TYs61T#7ibfB}$)^fy+Tf4hHDGRDeb=q-#hUjh?>>3*Y&Xq9IpdWOQ@6w1>2Y z+LhAmva?xPCr_lGMm_X=9o=xYE@z*HFRC-`IX7dUb_j?_KhpNCmf##_9=9aY;ANc9}kNRo;eCA*Fy0FtFD8NLa-1v*A^p3jJ6)UGsj<2StQIXo_((i-UiNWuKjWx%u#(aA}uG_lf%gmYIUVQcL{Pb}hPUdV} zir+H&j>N2+XWc|GUIZ^86`dCboC8JamXo_UIR39kB&oc7G{HT&Fe+-MHFa0oeesfC znx*yxZY$5Usi?nJ=xwZ=y#3Y5c&UPI|<^7R}qwN8by7jvZV7F+B8Z2RJS7S1}2`Rgwe;a{H6 zE_)ABtUdM*S@)_Y=>FnSZs@vNDwo%uA8ghXcBXfysLITwF)C2mOt}2QhoiID(AJ6g zInzy~-97Dw5s|0o_P%|c7;xLW^;4OD!RDz8PRsLxbuYBOi!x~Ke0k`3Vz{Bn^_t78 za#>u@cIGw zJ)T~*vF$sWEZeH&_FXRhm<~T}((bc=ex71js{EyoYU|tsg@0OgdP8Z~$GPkWfbG*+ z^E^W7^T+V7?{<8VaI1Rwz_{mdnDbcOT5I^k0o5! zb53~WS^tG2+tatT{RIB2J}q9g^w$PPU8-{Sdku@CJ0a>(O?$%9FG_djyy)23T>Pol zy?5@o=(+5+9ul<$|>rL{MurfNOpR>aM%|8^`i>FRL7 z?H4m+z9wf-iqC>(n_IKj_d1=!FB0^2?rFSLfIIrKjwZJa?q*SbvOW=HWPENThL3+4sACcdlP+ zy6>>LaSOL#5q0&owwljtK3Gikp=kti({a1*E-yHV(i3X{`Fo z%Yn=ajW5e@I=_;RS8vsM+~*cYoOtqiYBuIG?$UM-r_Gkd2cq7EedYWl`GMj3gm*CI z{h?V8q1S@CwxLHKFGgAXWG^FEGck1JLF!`=*vi;Ecf`moNyKP~ZP@g&y*uW>`sZWc z!@4YET1u7dq%DUn6fC5z&KeF&4V}r9;Uv{;RT3*b_?c5*f8JY-CkQt_aL`DisILEe z>46|eZDqZ(iW^@i(tm!IamDt6eEsCo$(egmZ&jkZf>v3?zL?~_(QvCcCiDD}#AQeO IFN>uA0g$#u^#A|> literal 0 HcmV?d00001 diff --git a/samples/AccessibilityService/res/raw/sound_screen_on.ogg b/samples/AccessibilityService/res/raw/sound_screen_on.ogg new file mode 100644 index 0000000000000000000000000000000000000000..bf3632e7bf6c2e38ab3cfc88ff0b3448ac649c2c GIT binary patch literal 13311 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 literal 0 HcmV?d00001 diff --git a/samples/AccessibilityService/res/raw/sound_view_focused_or_selected.ogg b/samples/AccessibilityService/res/raw/sound_view_focused_or_selected.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0100d2770b45f49627b8c9369b34fa3358e64ef2 GIT binary patch literal 6773 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 Q#NHc+{dvHlk&Ea5AN40arT_o{ literal 0 HcmV?d00001 diff --git a/samples/AccessibilityService/res/raw/sound_window_state_changed.ogg b/samples/AccessibilityService/res/raw/sound_window_state_changed.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0fec9de6fbf08e8bdfe6ceb91a4c87106b6dce05 GIT binary patch literal 3938 zcmeHKeN0nX7QZcD1%Y`o1qutM#e#i^gkohZA)B`H(Y{hX9<2+w>4CiKn-cbHuSQt(l+RT4ExOIrJeCX`7ar*C$-ER-Cys#MWcsc{#XO zD`_M(Vd_ST!sN2(nxNzjZE9?X?8@S)isRzp2z`Ks%*3ymNrQwDme+t^HW>8gpVUHOCsIUvPj~QT9Vu&eg-Nf!)$%P z;n_wN^`E06EPTKZ&pZRR0@>NPj)J`cLCa?Aaz{rWPx zx|+yu*=FUq3~zH=&OnHxXOdFRk=0i{=L=TJ4ZO#C*e|xvZZjlK#$A|7yhXj9pSv)f z{9VE|Y@2`oV31rfw2-gvzqDel2%Ia_^bai(xC2%hcj2dl zx^$$tpr!0*)onUhqQfK%w;>LedWyf&aWY5p#!NOS9L* zy!#1mdm^;0Cv0Vfw(dP=d0!4ZA36K38t>ZRjR?&)McizPfDJAKg62XOe1VQC{U;h> z)5DHmwK4k#0JP|i*V_w#Py+WV8JD3I$jnOlkWw+Eln1M_RqrZW6!QWF02_@kFNBii zn->4MQD;89y{;T9#k4OP^Rh}XTgjAoN?eOcG?yo)_}=}?`F#DUYx$dxGVA65hK-)O zS$|Z=rJ1^^>VgC5{-x2y7@)eFh^-0lI2HwUwf$Zn(9(|6ExgqrOerdN>Uu4!LO~1L z8O655zL%2K$)F1qwZE;a*P`P&f7BHNirCJW525Q<8NVX%%LwQoG9n*%)^yG`2Gc}V zq`OiTw+5jI*<2h#m0bBe#Kt*6t|vlpLazN~Y408tfi#^566t))I%XT2k%)f*T!iXvjO`d|dX+l$npy z=9Q{)%gQku&(N)L#tLq~bOn2XD%i5TpX%&$I{g$cQ*xOUr|JmBX#q~>^RYLa3$+}B zt=vL!J|7XmNg;snSQHAnE#1zMvvD4|0ysTXOU-3FElw!UM+W?J6u<*LI_C(qfk&x= zWTUw(>=A_a$$`CJq2DjnmfatQ=ozsUQWutZLzpV0xXU+m9c6932l%58W7q#%iXWOiQHf>`CUp8U!v}(p6nvl| z*T84=UEj{Rc1jymSkW)3=dW-F%_TFZv>2h;G~CXgV4F>a$1j_wWwQPu)`8f_E)yx| zzj`RgqaF83zPsGhC^2ptzjt7U?+Dp(`uyuZsEP9nEcJ`auUYET=NBIyc)=guUSae? zJCn8|5%=D?;H8cHaeK2# zGQqOnEs=NDF%QgKYQ76;5u1S+2{cb{hTlIhdWk<0VNsh?CB?yU3A{S*qx;eKXf0Pgi;^RARq0pMDr9;?ZOwSJS$q~^5vo1b9_DRZP?+wqG&B<^Pfe_|}K1WEz)797S zIy?4+zJ8m!uIuVkk5p<9okNd25b~u(MOiNS9bePnenaR~l)F$@VQO^$@QG0x4gD8vPJpcd literal 0 HcmV?d00001 diff --git a/samples/AccessibilityService/res/values/strings.xml b/samples/AccessibilityService/res/values/strings.xml new file mode 100644 index 000000000..a9913c240 --- /dev/null +++ b/samples/AccessibilityService/res/values/strings.xml @@ -0,0 +1,50 @@ + + + + + + + ClockBack + + + Increase hours + + + Increase minutes + + + Decrease hours + + + Decrease minutes + + + hour + + + hours + + + minute + + + minutes + + + Ringer audible + + + Ringer vibrate + + + Ringer silent + + + + + Screen on. Volume %1$s percent. + + + Screen off. Volume %1$s percent. + + diff --git a/samples/AccessibilityService/src/com/example/android/clockback/ClockBackService.java b/samples/AccessibilityService/src/com/example/android/clockback/ClockBackService.java new file mode 100644 index 000000000..5746716e1 --- /dev/null +++ b/samples/AccessibilityService/src/com/example/android/clockback/ClockBackService.java @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.clockback; + +import android.accessibilityservice.AccessibilityService; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.os.Handler; +import android.os.Message; +import android.os.Vibrator; +import android.speech.tts.TextToSpeech; +import android.util.Log; +import android.util.SparseArray; +import android.view.accessibility.AccessibilityEvent; + +import java.util.List; + +/** + * This class is an {@link AccessibilityService} that provides custom feedback + * for the Clock application that comes by default with Android devices. It + * demonstrates the following key features of the Android accessibility APIs: + *
    + *
  1. + * Simple demonstration of how to use the accessibility APIs. + *
  2. + *
  3. + * Hands-on example of various ways to utilize the accessibility API for + * providing alternative and complementary feedback. + *
  4. + *
  5. + * Providing application specific feedback — the service handles only + * accessibility events from the clock application. + *
  6. + *
  7. + * Providing dynamic, context-dependent feedback — feedback type changes + * depending on the ringer state. + *
  8. + *
  9. + * Application specific UI enhancement - application domain knowledge is + * utilized to enhance the provided feedback. + *
  10. + *
+ *

+ * + * Note: This code sample will work only on devices shipped with the default Clock + * application. If you are running Android 1.6 of Android 2.0 you should enable first + * ClockBack and then TalkBack since in these releases accessibility services are + * notified in the order of registration. + * + *

+ */ +public class ClockBackService extends AccessibilityService { + + /** Tag for logging from this service. */ + private static final String LOG_TAG = "ClockBackService"; + + // Fields for configuring how the system handles this accessibility service. + + /** Minimal timeout between accessibility events we want to receive. */ + private static final int EVENT_NOTIFICATION_TIMEOUT_MILLIS = 80; + + /** Packages we are interested in. + *

+ * + * Note: This code sample will work only on devices shipped with the + * default Clock application. + * + *

+ */ + // This works with AlarmClock and Clock whose package name changes in different releases + private static final String[] PACKAGE_NAMES = new String[] { + "com.android.alarmclock", "com.google.android.deskclock", "com.android.deskclock" + }; + + // Message types we are passing around. + + /** Speak. */ + private static final int MESSAGE_SPEAK = 1; + + /** Stop speaking. */ + private static final int MESSAGE_STOP_SPEAK = 2; + + /** Start the TTS service. */ + private static final int MESSAGE_START_TTS = 3; + + /** Stop the TTS service. */ + private static final int MESSAGE_SHUTDOWN_TTS = 4; + + /** Play an earcon. */ + private static final int MESSAGE_PLAY_EARCON = 5; + + /** Stop playing an earcon. */ + private static final int MESSAGE_STOP_PLAY_EARCON = 6; + + /** Vibrate a pattern. */ + private static final int MESSAGE_VIBRATE = 7; + + /** Stop vibrating. */ + private static final int MESSAGE_STOP_VIBRATE = 8; + + // Screen state broadcast related constants. + + /** Feedback mapping index used as a key for the screen-on broadcast. */ + private static final int INDEX_SCREEN_ON = 0x00000100; + + /** Feedback mapping index used as a key for the screen-off broadcast. */ + private static final int INDEX_SCREEN_OFF = 0x00000200; + + // Ringer mode change related constants. + + /** Feedback mapping index used as a key for normal ringer mode. */ + private static final int INDEX_RINGER_NORMAL = 0x00000400; + + /** Feedback mapping index used as a key for vibration ringer mode. */ + private static final int INDEX_RINGER_VIBRATE = 0x00000800; + + /** Feedback mapping index used as a key for silent ringer mode. */ + private static final int INDEX_RINGER_SILENT = 0x00001000; + + // Speech related constants. + + /** + * The queuing mode we are using - interrupt a spoken utterance before + * speaking another one. + */ + private static final int QUEUING_MODE_INTERRUPT = 2; + + /** The space string constant. */ + private static final String SPACE = " "; + + /** + * The class name of the number picker buttons with no text we want to + * announce in the Clock application. + */ + private static final String CLASS_NAME_NUMBER_PICKER_BUTTON_CLOCK = "android.widget.NumberPickerButton"; + + /** + * The class name of the number picker buttons with no text we want to + * announce in the AlarmClock application. + */ + private static final String CLASS_NAME_NUMBER_PICKER_BUTTON_ALARM_CLOCK = "com.android.internal.widget.NumberPickerButton"; + + /** + * The class name of the edit text box for hours and minutes we want to + * better announce. + */ + private static final String CLASS_NAME_EDIT_TEXT = "android.widget.EditText"; + + /** + * Mapping from integer to string resource id where the keys are generated + * from the {@link AccessibilityEvent#getText()}, + * {@link AccessibilityEvent#getItemCount()} and + * {@link AccessibilityEvent#getCurrentItemIndex()} properties. + *

+ * Note: In general, computing these mappings includes the widget position on + * the screen. This is fragile and should be used as a last resort since + * changing the layout could potentially change the widget position. This is + * a workaround since the widgets of interest are image buttons that do not + * have contentDescription attribute set (plus/minus buttons) or no other + * information in the accessibility event is available to distinguish them + * aside of their positions on the screen (hour/minute inputs).
+ * If you are owner of the target application (Clock in this case) you + * should add contentDescription attribute to all image buttons such that a + * screen reader knows how to speak them. For input fields (while not + * applicable for the hour and minute inputs since they are not empty) a + * hint text should be set to enable better announcement. + *

+ */ + private static final SparseArray sEventDataMappedStringResourceIds = new SparseArray(); + static { + sEventDataMappedStringResourceIds.put(110, R.string.value_increase_hours); + sEventDataMappedStringResourceIds.put(1140, R.string.value_increase_minutes); + sEventDataMappedStringResourceIds.put(1120, R.string.value_decrease_hours); + sEventDataMappedStringResourceIds.put(1160, R.string.value_decrease_minutes); + sEventDataMappedStringResourceIds.put(1111, R.string.value_hour); + sEventDataMappedStringResourceIds.put(1110, R.string.value_hours); + sEventDataMappedStringResourceIds.put(1151, R.string.value_minute); + sEventDataMappedStringResourceIds.put(1150, R.string.value_minutes); + } + + /** Mapping from integers to vibration patterns for haptic feedback. */ + private static final SparseArray sVibrationPatterns = new SparseArray(); + static { + sVibrationPatterns.put(AccessibilityEvent.TYPE_VIEW_CLICKED, new long[] { + 0L, 100L + }); + sVibrationPatterns.put(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED, new long[] { + 0L, 100L + }); + sVibrationPatterns.put(AccessibilityEvent.TYPE_VIEW_SELECTED, new long[] { + 0L, 15L, 10L, 15L + }); + sVibrationPatterns.put(AccessibilityEvent.TYPE_VIEW_FOCUSED, new long[] { + 0L, 15L, 10L, 15L + }); + sVibrationPatterns.put(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, new long[] { + 0L, 25L, 50L, 25L, 50L, 25L + }); + sVibrationPatterns.put(INDEX_SCREEN_ON, new long[] { + 0L, 10L, 10L, 20L, 20L, 30L + }); + sVibrationPatterns.put(INDEX_SCREEN_OFF, new long[] { + 0L, 30L, 20L, 20L, 10L, 10L + }); + } + + /** Mapping from integers to raw sound resource ids. */ + private static SparseArray sSoundsResourceIds = new SparseArray(); + static { + sSoundsResourceIds.put(AccessibilityEvent.TYPE_VIEW_CLICKED, R.raw.sound_view_clicked); + sSoundsResourceIds.put(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED, R.raw.sound_view_clicked); + sSoundsResourceIds.put(AccessibilityEvent.TYPE_VIEW_SELECTED, R.raw.sound_view_focused_or_selected); + sSoundsResourceIds.put(AccessibilityEvent.TYPE_VIEW_FOCUSED, R.raw.sound_view_focused_or_selected); + sSoundsResourceIds.put(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, R.raw.sound_window_state_changed); + sSoundsResourceIds.put(INDEX_SCREEN_ON, R.raw.sound_screen_on); + sSoundsResourceIds.put(INDEX_SCREEN_OFF, R.raw.sound_screen_off); + sSoundsResourceIds.put(INDEX_RINGER_SILENT, R.raw.sound_ringer_silent); + sSoundsResourceIds.put(INDEX_RINGER_VIBRATE, R.raw.sound_ringer_vibrate); + sSoundsResourceIds.put(INDEX_RINGER_NORMAL, R.raw.sound_ringer_normal); + } + + // Sound pool related member fields. + + /** Mapping from integers to earcon names - dynamically populated. */ + private final SparseArray mEarconNames = new SparseArray(); + + // Auxiliary fields. + + /** + * Handle to this service to enable inner classes to access the {@link Context}. + */ + Context mContext; + + /** The feedback this service is currently providing. */ + int mProvidedFeedbackType; + + /** Reusable instance for building utterances. */ + private final StringBuilder mUtterance = new StringBuilder(); + + // Feedback providing services. + + /** The {@link TextToSpeech} used for speaking. */ + private TextToSpeech mTts; + + /** The {@link AudioManager} for detecting ringer state. */ + private AudioManager mAudioManager; + + /** Vibrator for providing haptic feedback. */ + private Vibrator mVibrator; + + /** Flag if the infrastructure is initialized. */ + private boolean isInfrastructureInitialized; + + /** {@link Handler} for executing messages on the service main thread. */ + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_SPEAK: + String utterance = (String) message.obj; + mTts.speak(utterance, QUEUING_MODE_INTERRUPT, null); + return; + case MESSAGE_STOP_SPEAK: + mTts.stop(); + return; + case MESSAGE_START_TTS: + mTts = new TextToSpeech(mContext, new TextToSpeech.OnInitListener() { + public void onInit(int status) { + // Register here since to add earcons the TTS must be initialized and + // the receiver is called immediately with the current ringer mode. + registerBroadCastReceiver(); + } + }); + return; + case MESSAGE_SHUTDOWN_TTS: + mTts.shutdown(); + return; + case MESSAGE_PLAY_EARCON: + int resourceId = message.arg1; + playEarcon(resourceId); + return; + case MESSAGE_STOP_PLAY_EARCON: + mTts.stop(); + return; + case MESSAGE_VIBRATE: + int key = message.arg1; + long[] pattern = sVibrationPatterns.get(key); + mVibrator.vibrate(pattern, -1); + return; + case MESSAGE_STOP_VIBRATE: + mVibrator.cancel(); + return; + } + } + }; + + /** + * {@link BroadcastReceiver} for receiving updates for our context - device + * state. + */ + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { + int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, + AudioManager.RINGER_MODE_NORMAL); + configureForRingerMode(ringerMode); + } else if (Intent.ACTION_SCREEN_ON.equals(action)) { + provideScreenStateChangeFeedback(INDEX_SCREEN_ON); + } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { + provideScreenStateChangeFeedback(INDEX_SCREEN_OFF); + } else { + Log.w(LOG_TAG, "Registered for but not handling action " + action); + } + } + + /** + * Provides feedback to announce the screen state change. Such a change + * is turning the screen on or off. + * + * @param feedbackIndex The index of the feedback in the statically + * mapped feedback resources. + */ + private void provideScreenStateChangeFeedback(int feedbackIndex) { + // We take a specific action depending on the feedback we currently provide. + switch (mProvidedFeedbackType) { + case AccessibilityServiceInfo.FEEDBACK_SPOKEN: + String utterance = generateScreenOnOrOffUtternace(feedbackIndex); + mHandler.obtainMessage(MESSAGE_SPEAK, utterance).sendToTarget(); + return; + case AccessibilityServiceInfo.FEEDBACK_AUDIBLE: + mHandler.obtainMessage(MESSAGE_PLAY_EARCON, feedbackIndex, 0).sendToTarget(); + return; + case AccessibilityServiceInfo.FEEDBACK_HAPTIC: + mHandler.obtainMessage(MESSAGE_VIBRATE, feedbackIndex, 0).sendToTarget(); + return; + default: + throw new IllegalStateException("Unexpected feedback type " + + mProvidedFeedbackType); + } + } + }; + + @Override + public void onServiceConnected() { + if (isInfrastructureInitialized) { + return; + } + + mContext = this; + + // Send a message to start the TTS. + mHandler.sendEmptyMessage(MESSAGE_START_TTS); + + // Get the vibrator service. + mVibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE); + + // Get the AudioManager and configure according the current ring mode. + mAudioManager = (AudioManager) getSystemService(Service.AUDIO_SERVICE); + // In Froyo the broadcast receiver for the ringer mode is called back with the + // current state upon registering but in Eclair this is not done so we poll here. + int ringerMode = mAudioManager.getRingerMode(); + configureForRingerMode(ringerMode); + + // We are in an initialized state now. + isInfrastructureInitialized = true; + } + + @Override + public boolean onUnbind(Intent intent) { + if (isInfrastructureInitialized) { + // Stop the TTS service. + mHandler.sendEmptyMessage(MESSAGE_SHUTDOWN_TTS); + + // Unregister the intent broadcast receiver. + if (mBroadcastReceiver != null) { + unregisterReceiver(mBroadcastReceiver); + } + + // We are not in an initialized state anymore. + isInfrastructureInitialized = false; + } + return false; + } + + /** + * Registers the phone state observing broadcast receiver. + */ + private void registerBroadCastReceiver() { + // Create a filter with the broadcast intents we are interested in. + IntentFilter filter = new IntentFilter(); + filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + // Register for broadcasts of interest. + registerReceiver(mBroadcastReceiver, filter, null, null); + } + + /** + * Generates an utterance for announcing screen on and screen off. + * + * @param feedbackIndex The feedback index for looking up feedback value. + * @return The utterance. + */ + private String generateScreenOnOrOffUtternace(int feedbackIndex) { + // Get the announce template. + int resourceId = (feedbackIndex == INDEX_SCREEN_ON) ? R.string.template_screen_on + : R.string.template_screen_off; + String template = mContext.getString(resourceId); + + // Format the template with the ringer percentage. + int currentRingerVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING); + int maxRingerVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING); + int volumePercent = (100 / maxRingerVolume) * currentRingerVolume; + + // Let us round to five so it sounds better. + int adjustment = volumePercent % 10; + if (adjustment < 5) { + volumePercent -= adjustment; + } else if (adjustment > 5) { + volumePercent += (10 - adjustment); + } + + return String.format(template, volumePercent); + } + + /** + * Configures the service according to a ringer mode. Possible + * configurations: + *

+ * 1. {@link AudioManager#RINGER_MODE_SILENT}
+ * Goal: Provide only custom haptic feedback.
+ * Approach: Take over the haptic feedback by configuring this service to provide + * such and do so. This way the system will not call the default haptic + * feedback service KickBack.
+ * Take over the audible and spoken feedback by configuring this + * service to provide such feedback but not doing so. This way the system + * will not call the default spoken feedback service TalkBack and the + * default audible feedback service SoundBack. + *

+ *

+ * 2. {@link AudioManager#RINGER_MODE_VIBRATE}
+ * Goal: Provide custom audible and default haptic feedback.
+ * Approach: Take over the audible feedback and provide custom one.
+ * Take over the spoken feedback but do not provide such.
+ * Let some other service provide haptic feedback (KickBack). + *

+ *

+ * 3. {@link AudioManager#RINGER_MODE_NORMAL} + * Goal: Provide custom spoken, default audible and default haptic feedback.
+ * Approach: Take over the spoken feedback and provide custom one.
+ * Let some other services provide audible feedback (SounBack) and haptic + * feedback (KickBack). + *

+ * Note: In the above description an assumption is made that all default feedback + * services are enabled. Such services are TalkBack, SoundBack, and KickBack. + * Also the feature of defining a service as the default for a given feedback + * type will be available in Android 2.2 and above. For previous releases the package + * specific accessibility service must be registered first i.e. checked in the + * settings. + * + * @param ringerMode The device ringer mode. + */ + private void configureForRingerMode(int ringerMode) { + if (ringerMode == AudioManager.RINGER_MODE_SILENT) { + // When the ringer is silent we want to provide only haptic feedback. + mProvidedFeedbackType = AccessibilityServiceInfo.FEEDBACK_HAPTIC; + + // Take over the spoken and sound feedback so no such feedback is provided. + setServiceInfo(AccessibilityServiceInfo.FEEDBACK_HAPTIC + | AccessibilityServiceInfo.FEEDBACK_SPOKEN + | AccessibilityServiceInfo.FEEDBACK_AUDIBLE); + + // Use only an earcon to announce ringer state change. + mHandler.obtainMessage(MESSAGE_PLAY_EARCON, INDEX_RINGER_SILENT, 0).sendToTarget(); + } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { + // When the ringer is vibrating we want to provide only audible feedback. + mProvidedFeedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE; + + // Take over the spoken feedback so no spoken feedback is provided. + setServiceInfo(AccessibilityServiceInfo.FEEDBACK_AUDIBLE + | AccessibilityServiceInfo.FEEDBACK_SPOKEN); + + // Use only an earcon to announce ringer state change. + mHandler.obtainMessage(MESSAGE_PLAY_EARCON, INDEX_RINGER_VIBRATE, 0).sendToTarget(); + } else if (ringerMode == AudioManager.RINGER_MODE_NORMAL) { + // When the ringer is ringing we want to provide spoken feedback + // overriding the default spoken feedback. + mProvidedFeedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; + setServiceInfo(AccessibilityServiceInfo.FEEDBACK_SPOKEN); + + // Use only an earcon to announce ringer state change. + mHandler.obtainMessage(MESSAGE_PLAY_EARCON, INDEX_RINGER_NORMAL, 0).sendToTarget(); + } + } + + /** + * Sets the {@link AccessibilityServiceInfo} which informs the system how to + * handle this {@link AccessibilityService}. + * + * @param feedbackType The type of feedback this service will provide. + *

+ * Note: The feedbackType parameter is an bitwise or of all + * feedback types this service would like to provide. + *

+ */ + private void setServiceInfo(int feedbackType) { + AccessibilityServiceInfo info = new AccessibilityServiceInfo(); + // We are interested in all types of accessibility events. + info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK; + // We want to provide specific type of feedback. + info.feedbackType = feedbackType; + // We want to receive events in a certain interval. + info.notificationTimeout = EVENT_NOTIFICATION_TIMEOUT_MILLIS; + // We want to receive accessibility events only from certain packages. + info.packageNames = PACKAGE_NAMES; + setServiceInfo(info); + } + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + Log.i(LOG_TAG, mProvidedFeedbackType + " " + event.toString()); + + // Here we act according to the feedback type we are currently providing. + if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) { + mHandler.obtainMessage(MESSAGE_SPEAK, formatUtterance(event)).sendToTarget(); + } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_AUDIBLE) { + mHandler.obtainMessage(MESSAGE_PLAY_EARCON, event.getEventType(), 0).sendToTarget(); + } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_HAPTIC) { + mHandler.obtainMessage(MESSAGE_VIBRATE, event.getEventType(), 0).sendToTarget(); + } else { + throw new IllegalStateException("Unexpected feedback type " + mProvidedFeedbackType); + } + } + + @Override + public void onInterrupt() { + // Here we act according to the feedback type we are currently providing. + if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_SPOKEN) { + mHandler.obtainMessage(MESSAGE_STOP_SPEAK).sendToTarget(); + } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_AUDIBLE) { + mHandler.obtainMessage(MESSAGE_STOP_PLAY_EARCON).sendToTarget(); + } else if (mProvidedFeedbackType == AccessibilityServiceInfo.FEEDBACK_HAPTIC) { + mHandler.obtainMessage(MESSAGE_STOP_VIBRATE).sendToTarget(); + } else { + throw new IllegalStateException("Unexpected feedback type " + mProvidedFeedbackType); + } + } + + /** + * Formats an utterance from an {@link AccessibilityEvent}. + * + * @param event The event from which to format an utterance. + * @return The formatted utterance. + */ + private String formatUtterance(AccessibilityEvent event) { + StringBuilder utterance = mUtterance; + + // Clear the utterance before appending the formatted text. + utterance.setLength(0); + + List eventText = event.getText(); + + // We try to get the event text if such. + if (!eventText.isEmpty()) { + for (CharSequence subText : eventText) { + // Make 01 pronounced as 1 + if (subText.charAt(0) =='0') { + subText = subText.subSequence(1, subText.length()); + } + utterance.append(subText); + utterance.append(SPACE); + } + + // Here we do a bit of enhancement of the UI presentation by using the semantic + // of the event source in the context of the Clock application. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED + && CLASS_NAME_EDIT_TEXT.equals(event.getClassName())) { + // If the source is an edit text box and we have a mapping based on + // its position in the items of the container parent of the event source + // we append that value as well. We say "XX hours" and "XX minutes". + String resourceValue = getEventDataMappedStringResource(event); + if (resourceValue != null) { + utterance.append(resourceValue); + } + } + + return utterance.toString(); + } + + // There is no event text but we try to get the content description which is + // an optional attribute for describing a view (typically used with ImageView). + CharSequence contentDescription = event.getContentDescription(); + if (contentDescription != null) { + utterance.append(contentDescription); + return utterance.toString(); + } + + // No text and content description for the plus and minus buttons, so we lookup + // custom values based on the event's itemCount and currentItemIndex properties. + CharSequence className = event.getClassName(); + + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED + && (CLASS_NAME_NUMBER_PICKER_BUTTON_ALARM_CLOCK.equals(className) + || CLASS_NAME_NUMBER_PICKER_BUTTON_CLOCK.equals(className))) { + String resourceValue = getEventDataMappedStringResource(event); + utterance.append(resourceValue); + } + + return utterance.toString(); + } + + /** + * Returns a string resource mapped based on the accessibility event + * data, specifically the + * {@link AccessibilityEvent#getText()}, + * {@link AccessibilityEvent#getItemCount()}, and + * {@link AccessibilityEvent#getCurrentItemIndex()} properties. + * + * @param event The {@link AccessibilityEvent} to process. + * @return The mapped string if such exists, null otherwise. + */ + private String getEventDataMappedStringResource(AccessibilityEvent event) { + int lookupIndex = computeLookupIndex(event); + int resourceId = sEventDataMappedStringResourceIds.get(lookupIndex); + return getString(resourceId); + } + + /** + * Computes an index for looking up the custom text for views which either + * do not have text/content description or the position information + * is the only oracle for deciding from which widget was an accessibility + * event generated. The index is computed based on + * {@link AccessibilityEvent#getText()}, + * {@link AccessibilityEvent#getItemCount()}, and + * {@link AccessibilityEvent#getCurrentItemIndex()} properties. + * + * @param event The event from which to compute the index. + * @return The lookup index. + */ + private int computeLookupIndex(AccessibilityEvent event) { + int lookupIndex = event.getItemCount(); + int divided = event.getCurrentItemIndex(); + + while (divided > 0) { + lookupIndex *= 10; + divided /= 10; + } + + lookupIndex += event.getCurrentItemIndex(); + lookupIndex *= 10; + + // This is primarily for handling the zero hour/zero minutes cases + if (!event.getText().isEmpty() + && ("1".equals(event.getText().get(0).toString()) || "01".equals(event.getText() + .get(0).toString()))) { + lookupIndex++; + } + + return lookupIndex; + } + + /** + * Plays an earcon given its id. + * + * @param earconId The id of the earcon to be played. + */ + private void playEarcon(int earconId) { + String earconName = mEarconNames.get(earconId); + if (earconName == null) { + // We do not know the sound id, hence we need to load the sound. + int resourceId = sSoundsResourceIds.get(earconId); + earconName = "[" + earconId + "]"; + mTts.addEarcon(earconName, getPackageName(), resourceId); + mEarconNames.put(earconId, earconName); + } + + mTts.playEarcon(earconName, QUEUING_MODE_INTERRUPT, null); + } +}