fiore@0
|
1 /*
|
fiore@0
|
2 CCmI Diagram Editor for Android
|
fiore@0
|
3
|
fiore@0
|
4 Copyright (C) 2012 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
|
fiore@0
|
5
|
fiore@0
|
6 This program is free software: you can redistribute it and/or modify
|
fiore@0
|
7 it under the terms of the GNU General Public License as published by
|
fiore@0
|
8 the Free Software Foundation, either version 3 of the License, or
|
fiore@0
|
9 (at your option) any later version.
|
fiore@0
|
10
|
fiore@0
|
11 This program is distributed in the hope that it will be useful,
|
fiore@0
|
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
fiore@0
|
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
fiore@0
|
14 GNU General Public License for more details.
|
fiore@0
|
15
|
fiore@0
|
16 You should have received a copy of the GNU General Public License
|
fiore@0
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
fiore@0
|
18 */
|
fiore@0
|
19 package uk.ac.qmul.eecs.ccmi.activities;
|
fiore@0
|
20
|
fiore@0
|
21 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService;
|
fiore@0
|
22 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent;
|
fiore@0
|
23 import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder;
|
fiore@0
|
24 import uk.ac.qmul.eecs.ccmi.accessibility.LayoutSonifier;
|
fiore@1
|
25 import uk.ac.qmul.eecs.ccmi.utilities.ILogger;
|
fiore@0
|
26 import android.content.pm.PackageManager;
|
fiore@0
|
27 import android.media.AudioManager;
|
fiore@0
|
28 import android.os.Bundle;
|
fiore@0
|
29 import android.support.v4.app.FragmentActivity;
|
fiore@0
|
30 import android.view.MotionEvent;
|
fiore@0
|
31 import android.view.View;
|
fiore@0
|
32 import android.view.ViewGroup;
|
fiore@0
|
33 import android.view.ViewGroup.LayoutParams;
|
fiore@0
|
34 import android.view.WindowManager;
|
fiore@1
|
35 import android.widget.AbsListView;
|
fiore@0
|
36 import android.widget.Button;
|
fiore@0
|
37 import android.widget.LinearLayout;
|
fiore@0
|
38 import android.widget.ListAdapter;
|
fiore@0
|
39 import android.widget.ListView;
|
fiore@0
|
40 import android.widget.TextView;
|
fiore@0
|
41
|
fiore@0
|
42 /**
|
fiore@0
|
43 * An abstract accessible activity, displaying items of a list in a ListView and a text header.
|
fiore@0
|
44 */
|
fiore@0
|
45 public abstract class AccessibleActivity extends FragmentActivity {
|
fiore@0
|
46 private TextView header;
|
fiore@0
|
47 private ViewGroup mainLayout;
|
fiore@0
|
48 private Button scrollButton;
|
fiore@0
|
49 private boolean activityNameUttered = false;
|
fiore@0
|
50
|
fiore@0
|
51 /**
|
fiore@0
|
52 * the class used to generate audio and tactile cues when exploring the activity
|
fiore@0
|
53 */
|
fiore@0
|
54 protected AccessibilityService accessibilityService;
|
fiore@0
|
55 /**
|
fiore@0
|
56 * The list item list of the activity
|
fiore@0
|
57 */
|
fiore@0
|
58 protected ListView list;
|
fiore@0
|
59 /**
|
fiore@0
|
60 * used to generate dialogs when the user clicks or long-clicks on an item
|
fiore@0
|
61 */
|
fiore@0
|
62 protected AccessibleDialogBuilder dialogBuilder;
|
fiore@0
|
63
|
fiore@0
|
64 /** Called when the activity is first created. */
|
fiore@0
|
65 @Override
|
fiore@0
|
66 public void onCreate(Bundle savedInstanceState) {
|
fiore@0
|
67 super.onCreate(savedInstanceState);
|
fiore@0
|
68 // Set the hardware buttons to control the music
|
fiore@0
|
69 this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
fiore@0
|
70
|
fiore@0
|
71 /* makes the activity run on a full screen. */
|
fiore@0
|
72 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
fiore@0
|
73 WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
fiore@0
|
74 mainLayout = (ViewGroup)getLayoutInflater().inflate(R.layout.main, null);
|
fiore@0
|
75 setContentView(mainLayout);
|
fiore@0
|
76
|
fiore@0
|
77 dialogBuilder = new AccessibleDialogBuilder(this.getSupportFragmentManager());
|
fiore@0
|
78
|
fiore@0
|
79 /* place the main list in the view */
|
fiore@0
|
80 LinearLayout listLayout = (LinearLayout)findViewById(R.id.listView1);
|
fiore@0
|
81 /* avoid scrolling upon touching, we rather want the user to hear the content */
|
fiore@0
|
82 list = new ListView(this){
|
fiore@0
|
83 @Override
|
fiore@0
|
84 public boolean dispatchTouchEvent(MotionEvent ev){
|
fiore@0
|
85 //returning true prevents the scrolling action on the listview
|
fiore@0
|
86 if(ev.getAction()==MotionEvent.ACTION_MOVE && ev.getPointerCount() == 1)
|
fiore@0
|
87 return true;
|
fiore@0
|
88 return super.dispatchTouchEvent(ev);
|
fiore@0
|
89 }
|
fiore@0
|
90
|
fiore@0
|
91 @Override
|
fiore@0
|
92 public void setAdapter(final ListAdapter adapter){
|
fiore@0
|
93 super.setAdapter(adapter);
|
fiore@0
|
94 if(scrollButton != null){
|
fiore@0
|
95 post(new Runnable(){ // need to be posted in order to work, as the list is not rendered yet at this point
|
fiore@0
|
96 public void run(){
|
fiore@0
|
97 boolean thereIsMore = (list.getLastVisiblePosition() < adapter.getCount()-1);
|
fiore@0
|
98 scrollButton.setEnabled(thereIsMore);
|
fiore@0
|
99 scrollButton.setContentDescription(
|
fiore@0
|
100 thereIsMore ? "scroll button" : "scroll button disabled");
|
fiore@0
|
101 }
|
fiore@0
|
102 });
|
fiore@0
|
103 }
|
fiore@0
|
104 }
|
fiore@0
|
105 };
|
fiore@0
|
106 list.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
fiore@1
|
107 list.setOnScrollListener(new AbsListView.OnScrollListener(){
|
fiore@1
|
108
|
fiore@1
|
109 @Override
|
fiore@1
|
110 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
|
fiore@1
|
111
|
fiore@1
|
112 @Override
|
fiore@1
|
113 public void onScrollStateChanged(AbsListView view, int scrollState) {
|
fiore@1
|
114 switch(scrollState){
|
fiore@1
|
115 case AbsListView.OnScrollListener.SCROLL_STATE_FLING :
|
fiore@1
|
116 ILogger.log("user scroll: fling");
|
fiore@1
|
117 break;
|
fiore@1
|
118 case AbsListView.OnScrollListener.SCROLL_STATE_IDLE :
|
fiore@1
|
119 ILogger.log("user scroll: "+ view.getItemAtPosition(list.getFirstVisiblePosition()));
|
fiore@1
|
120 break;
|
fiore@1
|
121 case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL :
|
fiore@1
|
122 ILogger.log("user scroll: started scrolling");
|
fiore@1
|
123 break;
|
fiore@1
|
124 }
|
fiore@1
|
125 }
|
fiore@1
|
126 });
|
fiore@0
|
127 listLayout.addView(list);
|
fiore@0
|
128
|
fiore@0
|
129 /* init header with a button to scroll down an up the list */
|
fiore@0
|
130 header = (TextView)findViewById(R.id.txtHeader);
|
fiore@0
|
131 scrollButton = (Button)findViewById(R.id.scrollButton);
|
fiore@0
|
132
|
fiore@0
|
133 /* if the device supports double finger touch screen then the user will scroll by
|
fiore@0
|
134 * swiping with two fingers. Otherwise a button at the very bottom will be created that will scroll the
|
fiore@0
|
135 * list of one item each time it's pressed and when the bottom is reached it will scroll up to the top.
|
fiore@0
|
136 */
|
fiore@0
|
137 if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)){
|
fiore@0
|
138 mainLayout.removeView(scrollButton);
|
fiore@0
|
139 scrollButton = null;
|
fiore@0
|
140 }else{
|
fiore@0
|
141 scrollButton.setOnClickListener(new View.OnClickListener() {
|
fiore@0
|
142 boolean doOnceMore = true;
|
fiore@0
|
143
|
fiore@0
|
144 @Override
|
fiore@0
|
145 public void onClick(View v) {
|
fiore@0
|
146 if(list.getLastVisiblePosition() == list.getCount()-1){
|
fiore@0
|
147 /* when the last item becomes visible it's not entirely visible. So don't go immediately *
|
fiore@0
|
148 * to the top but let the user scroll once more and make it entirely visible */
|
fiore@0
|
149 if(doOnceMore){
|
fiore@0
|
150 doOnceMore = false;
|
fiore@0
|
151 accessibilityService.playSound(SoundEvent.V_OK, false);
|
fiore@0
|
152 list.smoothScrollToPosition(list.getLastVisiblePosition());
|
fiore@0
|
153 scrollButton.setText(R.string.scrollBtnUp);
|
fiore@0
|
154 }else{
|
fiore@0
|
155 doOnceMore = true;
|
fiore@0
|
156 accessibilityService.playSound(SoundEvent.V_ENDOFLIST, false);
|
fiore@0
|
157 list.smoothScrollToPosition(0);
|
fiore@0
|
158 scrollButton.setText(R.string.scrollBtnDown);
|
fiore@0
|
159 }
|
fiore@0
|
160 }else{
|
fiore@0
|
161 accessibilityService.playSound(SoundEvent.V_OK, false);
|
fiore@0
|
162 list.smoothScrollToPosition(list.getLastVisiblePosition());
|
fiore@0
|
163 }
|
fiore@0
|
164 }
|
fiore@0
|
165 });
|
fiore@0
|
166 }
|
fiore@0
|
167 }
|
fiore@0
|
168
|
fiore@0
|
169 @Override
|
fiore@0
|
170 protected void onResume(){
|
fiore@0
|
171 super.onResume();
|
fiore@0
|
172 accessibilityService = new AccessibilityService(this);
|
fiore@0
|
173 accessibilityService.loadSounds();
|
fiore@0
|
174 dialogBuilder.setAccessibilityService(accessibilityService);
|
fiore@0
|
175
|
fiore@0
|
176 }
|
fiore@0
|
177
|
fiore@0
|
178 /**
|
fiore@0
|
179 * When the activity is resumed the header text is uttered thought the text to speech sinthesizer
|
fiore@0
|
180 */
|
fiore@0
|
181 @Override
|
fiore@0
|
182 public void onPostResume(){
|
fiore@0
|
183 super.onPostResume();
|
fiore@0
|
184 header.post(new Runnable(){
|
fiore@0
|
185 @Override
|
fiore@0
|
186 public void run() {
|
fiore@0
|
187 if(!activityNameUttered){
|
fiore@0
|
188 accessibilityService.speak(getName()+". Displaying "+getHeaderText());
|
fiore@0
|
189 activityNameUttered = true;
|
fiore@0
|
190 }else{
|
fiore@0
|
191 accessibilityService.speak("Displaying "+getHeaderText());
|
fiore@0
|
192 }
|
fiore@0
|
193 }
|
fiore@0
|
194 });
|
fiore@0
|
195 }
|
fiore@0
|
196
|
fiore@0
|
197 @Override
|
fiore@0
|
198 protected void onPause(){
|
fiore@0
|
199 super.onPause();
|
fiore@0
|
200 accessibilityService.stopSound();
|
fiore@0
|
201 accessibilityService.dispose();
|
fiore@0
|
202 dialogBuilder.setAccessibilityService(null);
|
fiore@0
|
203 }
|
fiore@0
|
204
|
fiore@0
|
205 @Override
|
fiore@0
|
206 public boolean dispatchTouchEvent(MotionEvent evt){
|
fiore@0
|
207 //returning true prevents the scrolling action on the listview
|
fiore@0
|
208 if(evt.getAction()==MotionEvent.ACTION_MOVE && evt.getPointerCount() == 1){
|
fiore@0
|
209 LayoutSonifier.getInstance().onTouch(mainLayout, evt, accessibilityService);
|
fiore@0
|
210 return true;
|
fiore@0
|
211 }
|
fiore@0
|
212
|
fiore@0
|
213 if(evt.getAction() == MotionEvent.ACTION_UP)
|
fiore@0
|
214 LayoutSonifier.getInstance().onTouch(mainLayout, evt, accessibilityService);
|
fiore@0
|
215 return super.dispatchTouchEvent(evt);
|
fiore@0
|
216 }
|
fiore@0
|
217
|
fiore@0
|
218 /**
|
fiore@0
|
219 * Set the header text. The text is uttered when the user hover over the header.
|
fiore@0
|
220 *
|
fiore@0
|
221 * @param headerText the header text
|
fiore@0
|
222 */
|
fiore@0
|
223 protected void setHeaderText(CharSequence headerText){
|
fiore@0
|
224 header.setText(headerText);
|
fiore@0
|
225 }
|
fiore@0
|
226
|
fiore@0
|
227 /**
|
fiore@0
|
228 * Returns the header text. The text is uttered when the user hover over the header.
|
fiore@0
|
229 *
|
fiore@0
|
230 * @return the header text
|
fiore@0
|
231 */
|
fiore@0
|
232 protected CharSequence getHeaderText(){
|
fiore@0
|
233 return header.getText();
|
fiore@0
|
234 }
|
fiore@0
|
235
|
fiore@0
|
236 public abstract String getName();
|
fiore@0
|
237 }
|