PageRenderTime 33ms CodeModel.GetById 2ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

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