/talkback_preics/src/com/google/android/marvin/talkback/formatter/calendar/DayOrWeekOrAgendaViewSelectedFormatter.java

http://eyes-free.googlecode.com/ · Java · 368 lines · 239 code · 43 blank · 86 comment · 37 complexity · 7d5689402b6d8eabf58427ee3f56abb1 MD5 · raw file

  1. /*
  2. * Copyright (C) 2010 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.google.android.marvin.talkback.formatter.calendar;
  17. import com.google.android.marvin.talkback.Formatter;
  18. import com.google.android.marvin.talkback.R;
  19. import com.google.android.marvin.talkback.Utterance;
  20. import android.content.Context;
  21. import android.database.Cursor;
  22. import android.net.Uri;
  23. import android.os.Build;
  24. import android.os.Bundle;
  25. import android.text.TextUtils;
  26. import android.text.format.DateFormat;
  27. import android.text.format.DateUtils;
  28. import android.util.SparseArray;
  29. import android.view.accessibility.AccessibilityEvent;
  30. import java.util.List;
  31. /**
  32. * This class is a custom formatter for
  33. * {@link AccessibilityEvent#TYPE_VIEW_SELECTED} events from the Day/Weekly view
  34. * of the Google Calendar application.
  35. */
  36. public final class DayOrWeekOrAgendaViewSelectedFormatter implements Formatter {
  37. private static final int SDK_INT = Build.VERSION.SDK_INT;
  38. private static final int GINGERBREAD = 9;
  39. private static final int HONEYCOMB = 10;
  40. private static String CLASS_NAME_AGENDA_VIEW = "";
  41. static {
  42. switch (SDK_INT) {
  43. case GINGERBREAD:
  44. CLASS_NAME_AGENDA_VIEW = "com.android.calendar.AgendaListView";
  45. break;
  46. case HONEYCOMB:
  47. CLASS_NAME_AGENDA_VIEW = "com.android.calendar.agenda.AgendaListView";
  48. break;
  49. }
  50. }
  51. private static final char SPACE = ' ';
  52. private static final char PERIOD = '.';
  53. private static final char COMMA = ',';
  54. private static final String CLASS_NAME_DAY_VIEW = "com.android.calendar.DayView";
  55. public static final Uri CONTENT_URI_CALENDARS = Uri
  56. .parse("content://com.android.calendar/calendars");
  57. private static final String COLOR = "color";
  58. private static final String DISPLAY_NAME = "displayName";
  59. private static final String SELECTED = "selected";
  60. private static final int COLUMN_INDEX_COLOR = 0;
  61. private static final int COLUMN_INDEX_DISPLAY_NAME = 1;
  62. private static final String CALENDAR_EVENT_COLOR = "color";
  63. private static final String CALENDAR_EVENT_TITLE = "title";
  64. private static final String CALENDAR_EVENT_LOCATION = "location";
  65. private static final String CALENDAR_EVENT_START_MILLIS = "startMillis";
  66. private static final String CALENDAR_EVENT_END_MILLIS = "endMillis";
  67. private static final String[] PROJECTION = new String[] {
  68. COLOR, DISPLAY_NAME
  69. };
  70. private final SparseArray<String> mColorToDisplayNameMap = new SparseArray<String>();
  71. private String mLastTimeFragment;
  72. private String mLastDateFragment;
  73. @Override
  74. public void format(AccessibilityEvent event, Context context, Utterance utterance, Object args) {
  75. String className = event.getClassName().toString();
  76. if (CLASS_NAME_AGENDA_VIEW.equals(className)) {
  77. formatAgendaViewSelected(event, context, utterance);
  78. } else {
  79. formatDayOrWeekViewSelected(event, context, utterance);
  80. }
  81. }
  82. /**
  83. * Formats utterance for announcing DayView or WeekView selection events.
  84. *
  85. * @param event The processed {@link AccessibilityEvent}.
  86. * @param context For accessing resources.
  87. * @param utterance The utterance to format.
  88. */
  89. private void formatDayOrWeekViewSelected(AccessibilityEvent event, Context context,
  90. Utterance utterance) {
  91. StringBuilder textBuilder = utterance.getText();
  92. appendSelectedRange(context, event, textBuilder);
  93. appendSelectedEventIndexAnouncement(context, event, textBuilder);
  94. appendSelectedEventDetails(context, event, textBuilder);
  95. }
  96. /**
  97. * Formats utterance for announcing AgendaView selection events.
  98. *
  99. * @param event The processed {@link AccessibilityEvent}.
  100. * @param context For accessing resources.
  101. * @param utterance The utterance to format.
  102. */
  103. private void formatAgendaViewSelected(AccessibilityEvent event, Context context,
  104. Utterance utterance) {
  105. StringBuilder textBuilder = utterance.getText();
  106. appendDisplayName(context, event, textBuilder);
  107. appendEventText(event, textBuilder);
  108. }
  109. /**
  110. * Appends the event text.
  111. *
  112. * @param event The event being processed.
  113. * @param textBuilder The builder to which to append the announcement.
  114. */
  115. private void appendEventText(AccessibilityEvent event, StringBuilder textBuilder) {
  116. List<CharSequence> text = event.getText();
  117. for (CharSequence subText : text) {
  118. textBuilder.append(subText);
  119. textBuilder.append(SPACE);
  120. }
  121. }
  122. /**
  123. * Appends announcement for the selected time range.
  124. *
  125. * @param context For accessing resources.
  126. * @param event The event being processed.
  127. * @param textBuilder The builder to which to append the announcement.
  128. */
  129. private void appendSelectedRange(Context context, AccessibilityEvent event,
  130. StringBuilder textBuilder) {
  131. String eventText = event.getText().get(0).toString();
  132. if (TextUtils.isEmpty(eventText)) {
  133. return;
  134. }
  135. String className = event.getClassName().toString();
  136. if (CLASS_NAME_DAY_VIEW.equals(className)) {
  137. String timeFragment = eventText;
  138. switch (SDK_INT) {
  139. case GINGERBREAD:
  140. if (!timeFragment.equals(mLastTimeFragment)) {
  141. mLastTimeFragment = timeFragment;
  142. textBuilder.append(eventText);
  143. textBuilder.append(PERIOD);
  144. }
  145. break;
  146. case HONEYCOMB:
  147. String dateFragment = null;
  148. int firstCommaIndex = eventText.indexOf(COMMA);
  149. if (firstCommaIndex > -1) {
  150. timeFragment = eventText.substring(0, firstCommaIndex);
  151. dateFragment = eventText.substring(firstCommaIndex + 1);
  152. }
  153. if (!timeFragment.equals(mLastTimeFragment)) {
  154. mLastTimeFragment = timeFragment;
  155. textBuilder.append(timeFragment);
  156. if (dateFragment == null) {
  157. textBuilder.append(PERIOD);
  158. } else {
  159. textBuilder.append(COMMA);
  160. }
  161. }
  162. if (dateFragment != null) {
  163. textBuilder.append(dateFragment);
  164. textBuilder.append(PERIOD);
  165. }
  166. break;
  167. }
  168. } else {
  169. int firstCommaIndex = eventText.indexOf(',');
  170. String timeFragment = eventText.substring(0, firstCommaIndex);
  171. String dateFragment = eventText.substring(firstCommaIndex + 1);
  172. if (!dateFragment.equals(mLastDateFragment)) {
  173. mLastDateFragment = dateFragment;
  174. textBuilder.append(dateFragment);
  175. int todayEventCount = event.getAddedCount();
  176. if (todayEventCount > 0) {
  177. textBuilder.append(COMMA);
  178. textBuilder.append(SPACE);
  179. appendEventCountAnnouncement(context, event, textBuilder);
  180. }
  181. textBuilder.append(PERIOD);
  182. textBuilder.append(SPACE);
  183. }
  184. if (!timeFragment.equals(mLastTimeFragment)) {
  185. mLastTimeFragment = timeFragment;
  186. textBuilder.append(timeFragment);
  187. if (event.getItemCount() > 0) {
  188. textBuilder.append(COMMA);
  189. textBuilder.append(SPACE);
  190. }
  191. }
  192. }
  193. }
  194. /**
  195. * Appends announcement for the index of the selected event in case of
  196. * multiple events in the selected time range.
  197. *
  198. * @param context For accessing resources.
  199. * @param event The event being processed.
  200. * @param textBuilder The builder to which to append the announcement.
  201. */
  202. private void appendSelectedEventIndexAnouncement(Context context, AccessibilityEvent event,
  203. StringBuilder textBuilder) {
  204. int eventCount = event.getItemCount();
  205. if (eventCount > 1) {
  206. textBuilder.append(SPACE);
  207. appendEventCountAnnouncement(context, event, textBuilder);
  208. textBuilder.append(PERIOD);
  209. textBuilder.append(SPACE);
  210. }
  211. }
  212. /**
  213. * Appends announcement for event count.
  214. *
  215. * @param context For accessing resources.
  216. * @param event The processed event.
  217. * @param textBuilder The builder to which to append the announcement.
  218. */
  219. private void appendEventCountAnnouncement(Context context, AccessibilityEvent event,
  220. StringBuilder textBuilder) {
  221. int eventIndex = event.getCurrentItemIndex() + 1;
  222. int eventCount = event.getItemCount();
  223. textBuilder.append(context.getString(R.string.template_announce_item_index, eventIndex,
  224. eventCount));
  225. textBuilder.append(SPACE);
  226. textBuilder.append(context.getResources().getQuantityString(R.plurals.plural_event,
  227. eventCount));
  228. }
  229. /**
  230. * Appends announcement for the selected event details i.e. event content.
  231. *
  232. * @param context For accessing resources.
  233. * @param event The event being processed.
  234. * @param textBuilder The builder to which to append the announcement.
  235. */
  236. private void appendSelectedEventDetails(Context context, AccessibilityEvent event,
  237. StringBuilder textBuilder) {
  238. Bundle parcelableData = (Bundle) event.getParcelableData();
  239. if (parcelableData == null) {
  240. return;
  241. }
  242. // append account description if more than one account is shown
  243. appendDisplayName(context, event, textBuilder);
  244. // append the event title
  245. CharSequence title = parcelableData.getCharSequence(CALENDAR_EVENT_TITLE);
  246. if (!TextUtils.isEmpty(title)) {
  247. textBuilder.append(SPACE);
  248. textBuilder.append(title);
  249. textBuilder.append(PERIOD);
  250. }
  251. // append time
  252. long startMillis = parcelableData.getLong(CALENDAR_EVENT_START_MILLIS);
  253. if (startMillis > 0) {
  254. long endMillis = parcelableData.getLong(CALENDAR_EVENT_END_MILLIS);
  255. int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE
  256. | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL
  257. | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
  258. if (DateFormat.is24HourFormat(context)) {
  259. flags |= DateUtils.FORMAT_24HOUR;
  260. }
  261. String timeRange = DateUtils.formatDateRange(context, startMillis, endMillis, flags);
  262. textBuilder.append(SPACE);
  263. textBuilder.append(timeRange);
  264. textBuilder.append(PERIOD);
  265. }
  266. // append the event location
  267. CharSequence location = parcelableData.getCharSequence(CALENDAR_EVENT_LOCATION);
  268. if (!TextUtils.isEmpty(location)) {
  269. textBuilder.append(SPACE);
  270. textBuilder.append(location);
  271. textBuilder.append(PERIOD);
  272. }
  273. }
  274. /**
  275. * Appends the display name from whose calendar the event comes.
  276. *
  277. * @param context For accessing resources.
  278. * @param event The event being processed.
  279. * @param textBuilder The builder to which to append the announcement.
  280. */
  281. private void appendDisplayName(Context context, AccessibilityEvent event,
  282. StringBuilder textBuilder) {
  283. Bundle parcelableData = (Bundle) event.getParcelableData();
  284. if (parcelableData == null) {
  285. return;
  286. }
  287. // append account description if more than one account is shown
  288. int color = parcelableData.getInt(CALENDAR_EVENT_COLOR);
  289. String accountDescription = mColorToDisplayNameMap.get(color);
  290. if (accountDescription == null) {
  291. reloadColorToDisplayNameMap(context);
  292. }
  293. if (mColorToDisplayNameMap.size() > 1) {
  294. accountDescription = mColorToDisplayNameMap.get(color);
  295. if (accountDescription != null) {
  296. textBuilder.append(context.getString(R.string.value_owner));
  297. textBuilder.append(COMMA);
  298. textBuilder.append(SPACE);
  299. textBuilder.append(accountDescription);
  300. textBuilder.append(PERIOD);
  301. }
  302. }
  303. }
  304. /**
  305. * Reloads the color to display name map used to announce the event owner.
  306. *
  307. * @param context For accessing resources.
  308. */
  309. private void reloadColorToDisplayNameMap(Context context) {
  310. Cursor cursor = context.getContentResolver().query(CONTENT_URI_CALENDARS, PROJECTION,
  311. SELECTED + "=?", new String[] {
  312. "1"
  313. }, null);
  314. if (cursor == null) {
  315. return;
  316. }
  317. while (!cursor.isLast()) {
  318. cursor.moveToNext();
  319. int color = cursor.getInt(COLUMN_INDEX_COLOR);
  320. String dispayName = cursor.getString(COLUMN_INDEX_DISPLAY_NAME);
  321. mColorToDisplayNameMap.put(color, dispayName);
  322. }
  323. cursor.close();
  324. }
  325. }