PageRenderTime 131ms CodeModel.GetById 49ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 0ms

/talkback_preics/src/com/google/android/marvin/talkback/TalkBackService.java

http://eyes-free.googlecode.com/
Java | 1779 lines | 960 code | 239 blank | 580 comment | 234 complexity | 1f1106e0d79f39c25b9f2c6cf62a24e3 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2 * Copyright (C) 2009 The Android Open Source Project
   3 *
   4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
   5 * use this file except in compliance with the License. You may obtain a copy of
   6 * 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, WITHOUT
  12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13 * License for the specific language governing permissions and limitations under
  14 * the License.
  15 */
  16
  17package com.google.android.marvin.talkback;
  18
  19import com.google.android.marvin.commands.Command;
  20import com.google.android.marvin.commands.CommandConstants;
  21import com.google.android.marvin.commands.CommandsManager;
  22import com.google.android.marvin.commands.impls.SavingPhoneStateListener;
  23import com.google.android.marvin.talkback.ProximitySensor.ProximityChangeListener;
  24import com.google.android.marvin.talkback.commands.TalkBackCommands;
  25import com.google.tts.TextToSpeechBeta;
  26
  27import android.accessibilityservice.AccessibilityService;
  28import android.accessibilityservice.AccessibilityServiceInfo;
  29import android.app.ActivityManager;
  30import android.app.Notification;
  31import android.app.NotificationManager;
  32import android.app.PendingIntent;
  33import android.app.Service;
  34import android.content.BroadcastReceiver;
  35import android.content.Context;
  36import android.content.Intent;
  37import android.content.IntentFilter;
  38import android.content.SharedPreferences;
  39import android.content.pm.PackageManager;
  40import android.content.pm.ResolveInfo;
  41import android.content.pm.PackageManager.NameNotFoundException;
  42import android.media.AudioManager;
  43import android.net.Uri;
  44import android.os.Build;
  45import android.os.Environment;
  46import android.os.Handler;
  47import android.os.Message;
  48import android.os.Parcelable;
  49import android.os.SystemClock;
  50import android.preference.PreferenceManager;
  51import android.speech.tts.TextToSpeech;
  52import android.telephony.PhoneStateListener;
  53import android.telephony.TelephonyManager;
  54import android.text.format.DateFormat;
  55import android.text.format.DateUtils;
  56import android.util.Log;
  57import android.view.accessibility.AccessibilityEvent;
  58import android.widget.EditText;
  59
  60import java.io.BufferedReader;
  61import java.io.FileNotFoundException;
  62import java.io.FileOutputStream;
  63import java.io.FileReader;
  64import java.io.IOException;
  65import java.lang.Thread.UncaughtExceptionHandler;
  66import java.lang.reflect.InvocationTargetException;
  67import java.lang.reflect.Method;
  68import java.util.ArrayList;
  69import java.util.Arrays;
  70import java.util.Collection;
  71import java.util.Date;
  72import java.util.HashMap;
  73import java.util.Iterator;
  74import java.util.List;
  75import java.util.Map;
  76import java.util.regex.Matcher;
  77import java.util.regex.Pattern;
  78
  79/**
  80 * {@link AccessibilityService} that provides spoken feedback.
  81 *
  82 * @author svetoslavganov@google.com (Svetoslav R. Ganov)
  83 * @author clchen@google.com (Charles L. Chen)
  84 */
  85public class TalkBackService extends AccessibilityService {
  86
  87    // BEGIN WORKAROUND FOR ECLAIRE COMPATIBILITY
  88    
  89    private static Method TextToSpeech_getDefaultEngine;
  90
  91    private static Method TextToSpeech_setEngineByPackageName;
  92    
  93    static {
  94        initCompatibility();
  95    }
  96
  97    private static void initCompatibility() {
  98        try {
  99            TextToSpeech_getDefaultEngine = TextToSpeech.class.getMethod("getDefaultEngine", new Class[] {});
 100            TextToSpeech_setEngineByPackageName = TextToSpeech.class.getMethod("setEngineByPackageName", new Class[] {
 101                    String.class
 102            });
 103            /* success, this is a newer device */
 104        } catch (NoSuchMethodException nsme) {
 105            /* failure, must be older device */
 106        }
 107    }
 108
 109    private static String getDefaultEngine(TextToSpeech tts) {
 110        try {
 111            Object retobj = TextToSpeech_getDefaultEngine.invoke(tts);
 112            return (String) retobj;
 113        } catch (IllegalAccessException e) {
 114            e.printStackTrace();
 115        } catch (IllegalArgumentException e) {
 116            e.printStackTrace();
 117        } catch (InvocationTargetException e) {
 118            e.printStackTrace();
 119        }
 120        return null;
 121    }
 122
 123    private static int setEngineByPackageName(TextToSpeech tts, String packageName) {
 124        try {
 125            Object retobj = TextToSpeech_setEngineByPackageName.invoke(tts, packageName);
 126            return (Integer) retobj;
 127        } catch (IllegalAccessException e) {
 128            e.printStackTrace();
 129        } catch (IllegalArgumentException e) {
 130            e.printStackTrace();
 131        } catch (InvocationTargetException e) {
 132            e.printStackTrace();
 133        }
 134        return -1;
 135    }
 136
 137    // END OF WORKAROUND FOR ECLAIRE COMPATIBILITY
 138    
 139    /**
 140     * {@link Intent} broadcast action for announcing the notifications state.
 141     * </p> Note: Sending intent broadcast commands to TalkBack must be
 142     * performed through {@link Context#sendBroadcast(Intent, String)}
 143     */
 144    public static final String ACTION_ANNOUNCE_STATUS_SUMMARY_COMMAND = "com.google.android.marvin.talkback.ACTION_ANNOUNCE_STATUS_SUMMARY_COMMAND";
 145
 146    public static final String TALKBACK_USER_EVENT_COMMAND = "com.google.android.marvin.commands.TALKBACK_USER_EVENT_COMMAND";
 147
 148    /**
 149     * {@link Intent} broadcast action for resetting the TalkBack service. </p>
 150     * Note: Sending intent broadcast commands to TalkBack must be performed
 151     * through {@link Context#sendBroadcast(Intent, String)}
 152     */
 153    public static final String ACTION_RESET_TALKBACK_COMMAND = "com.google.android.marvin.talkback.ACTION_RESET_TALKBACK_COMMAND";
 154    
 155    /**
 156     * {@link Intent} broadcast action for querying the state of TalkBack. </p>
 157     * Note: Sending intent broadcast commands to TalkBack must be performed
 158     * through {@link Context#sendBroadcast(Intent, String)}
 159     */
 160    @Deprecated
 161    // TODO(caseyburkhardt): Remove when we decide to no longer support intent broadcasts for
 162    // querying the current state of TalkBack.
 163    public static final String ACTION_QUERY_TALKBACK_ENABLED_COMMAND = "com.google.android.marvin.talkback.ACTION_QUERY_TALKBACK_ENABLED_COMMAND";
 164
 165    /**
 166     * Result that TalkBack is enabled.
 167     *
 168     * @see #ACTION_QUERY_TALKBACK_ENABLED_COMMAND
 169     */
 170    public static final int RESULT_TALKBACK_ENABLED = 0x00000001;
 171
 172    /**
 173     * Result that TalkBack is disabled.
 174     *
 175     * @see #ACTION_QUERY_TALKBACK_ENABLED_COMMAND
 176     */
 177    public static final int RESULT_TALKBACK_DISABLED = 0x00000002;
 178
 179    /**
 180     * Permission to send {@link Intent} broadcast commands to TalkBack.
 181     */
 182    public static final String PERMISSION_SEND_INTENT_BROADCAST_COMMANDS_TO_TALKBACK = "com.google.android.marvin.talkback.PERMISSION_SEND_INTENT_BROADCAST_COMMANDS_TO_TALKBACK";
 183
 184    /**
 185     * Tag for logging.
 186     */
 187    private static final String LOG_TAG = "TalkBackService";
 188
 189    /**
 190     * To account for SVox camel-case trouble.
 191     */
 192    private static final Pattern sCamelCasePrefixPattern = Pattern.compile("([a-z0-9])([A-Z])");
 193
 194    /**
 195     * To account for SVox camel-case trouble.
 196     */
 197    private static final Pattern sCamelCaseSuffixPattern = Pattern.compile("([A-Z])([a-z0-9])");
 198
 199    /**
 200     * To recognize strings with only capital letters, that have no vowels or
 201     * are three letters or shorter. Use this to find acronyms that should be
 202     * spelled out instead of spoken.
 203     */
 204    private static final Pattern sAcronymPattern = Pattern.compile("\\b(([A-Z&&[^AEIOU]]{2,})|([A-Z]{2,3}))\\b");
 205
 206    /**
 207     * To add spaces between two consecutive capital letters.
 208     */
 209    private static final Pattern sConsecutiveCapsPattern = Pattern.compile("([A-Z])(?=[A-Z])");
 210
 211    /**
 212     * Manages the pending notifications.
 213     */
 214    private static final NotificationCache sNotificationCache = new NotificationCache();
 215
 216    /**
 217     * Timeout for waiting the events to settle down before speaking
 218     */
 219    private static final long EVENT_TIMEOUT = 200;
 220
 221    /**
 222     * Timeout for waiting the events to settle down before speaking
 223     */
 224    private static final long EVENT_TIMEOUT_IN_CALL_SCREEN = 3000;
 225
 226    /**
 227     * The class name of the in-call screen.
 228     */
 229    private static final String CLASS_NAME_IN_CALL_SCREEN = "com.android.phone.InCallScreen";
 230
 231    /**
 232     * The package name of the Accessibility Settings Manager
 233     */
 234    private static final String SETTINGS_MANAGER_PACKAGE = "com.marvin.preferences";
 235
 236    /**
 237     * Speak action.
 238     */
 239    private static final int WHAT_SPEAK = 1;
 240
 241    /**
 242     * Speak while the phone is ringing action.
 243     */
 244    private static final int WHAT_SPEAK_WHILE_IN_CALL = 2;
 245
 246    /**
 247     * Stop speaking action.
 248     */
 249    private static final int WHAT_STOP_ALL_SPEAKING = 3;
 250
 251    /**
 252     * Start the TTS service.
 253     */
 254    private static final int WHAT_START_TTS = 4;
 255
 256    /**
 257     * Stop the TTS service.
 258     */
 259    private static final int WHAT_SHUTDOWN_TTS = 5;
 260
 261    /**
 262     * Switch TTS systems (from or to TTS Extended).
 263     */
 264    private static final int WHAT_SWITCH_TTS = 6;
 265
 266    /**
 267     * Space string constant.
 268     */
 269    private static final String SPACE = " ";
 270
 271    /**
 272     * Opening bracket character constant.
 273     */
 274    private static final char OPEN_SQUARE_BRACKET = '[';
 275
 276    /**
 277     * Closing bracket character constant.
 278     */
 279    private static final char CLOSE_SQUARE_BRACKET = ']';
 280
 281    /**
 282     * The name of the contacts package used to fix a specific behavior in
 283     * Dialer
 284     */
 285    private static final String PACKAGE_NAME_CONTACTS = "com.android.contacts";
 286
 287    /**
 288     * Prefix for utterance IDs.
 289     */
 290    private static final String UTTERANCE_ID_PREFIX = "talkback_";
 291
 292    /**
 293     * {@link IntentFilter} with all commands that can be executed by third
 294     * party applications or services via intent broadcasting.
 295     */
 296    private static final IntentFilter sCommandInterfaceIntentFilter = new IntentFilter();
 297    static {
 298        sCommandInterfaceIntentFilter.addAction(ACTION_ANNOUNCE_STATUS_SUMMARY_COMMAND);
 299        // add other command intents here
 300    }
 301
 302    /**
 303     * Notification ID for the "TalkBack Settings" notification
 304     */
 305    private static final int SETTINGS_NOTIFICATION_ID = 1;
 306
 307    /**
 308     * Notification ID for the "TalkBack Crash Report" notification
 309     */
 310    private static final int CRASH_NOTIFICATION_ID = 2;
 311
 312    /**
 313     * Queuing mode - interrupt the spoken utterance before speaking another one.
 314     */
 315    public static final int QUEUING_MODE_INTERRUPT = TextToSpeech.QUEUE_FLUSH;
 316
 317    /**
 318     * Queuing mode - queue the utterance to be spoken.
 319     */
 320    public static final int QUEUING_MODE_QUEUE = TextToSpeech.QUEUE_ADD;
 321
 322    /**
 323     * Queuing mode - compute the queuing mode base on previous event context.
 324     */
 325    public static final int QUEUING_MODE_COMPUTE_FROM_EVENT_CONTEXT = 2;
 326
 327    /**
 328     * Queuing mode - uninterruptible utterance.
 329     */
 330    public static final int QUEUING_MODE_UNINTERRUPTIBLE = 3;
 331
 332    /**
 333     * The maximal size to the queue of cached events.
 334     */
 335    private static final int EVENT_QUEUE_MAX_SIZE = 2;
 336
 337    /**
 338     * Ringer preference - speak at all ringer volumes.
 339     */
 340    public static final int PREF_RINGER_ALL = 0;
 341
 342    /**
 343     * Ringer preference - speak unless silent mode.
 344     */
 345    public static final int PREF_RINGER_NOT_SILENT = 1;
 346
 347    /**
 348     * Ringer preference - speak unless silent or vibrate mode.
 349     */
 350    public static final int PREF_RINGER_NOT_SILENT_OR_VIBRATE = 2;
 351
 352    /**
 353     * Ringer preference - default.
 354     */
 355    public static final int PREF_RINGER_DEFAULT = PREF_RINGER_ALL;
 356
 357    /**
 358     * Screen preference - allow speech when screen is off.
 359     */
 360    public static final int PREF_SCREEN_OFF_ALLOWED = 0;
 361
 362    /**
 363     * Screen preference - do not allow speech when screen is off.
 364     */
 365    public static final int PREF_SCREEN_OFF_DISALLOWED = 1;
 366
 367    /**
 368     * Screen preference - default.
 369     */
 370    public static final int PREF_SCREEN_DEFAULT = PREF_SCREEN_OFF_ALLOWED;
 371
 372    /**
 373     * Caller ID preference - default.
 374     */
 375    public static final boolean PREF_CALLER_ID_DEFAULT = true;
 376
 377    /**
 378     * TTS Extended preference - default.
 379     */
 380    public static final boolean PREF_TTS_EXTENDED_DEFAULT = false;
 381
 382    /**
 383     * Proximity Sensor preference - default.
 384     */
 385    public static final boolean PREF_PROXIMITY_DEFAULT = true;
 386
 387    /**
 388     * Period for the proximity sensor to remain active after last utterance (in milliseconds).
 389     */
 390    public static final long PROXIMITY_SENSOR_CUTOFF_THRESHOLD = 1000;
 391
 392    /**
 393     * The filename used for logging crash reports.
 394     */
 395    private static final String TALKBACK_CRASH_LOG = "talkback_crash.log";
 396
 397    /**
 398     * The email address that receives TalkBack crash reports.
 399     */
 400    private static final String[] CRASH_REPORT_EMAILS = {"eyes.free.crash.reports@gmail.com"};
 401
 402    /**
 403     * Flag if the infrastructure has been initialized.
 404     */
 405    private static boolean sInfrastructureInitialized = false;
 406
 407    /**
 408     * Flag if the TTS service has been initialized.
 409     */
 410    private static boolean sTtsInitialized = false;
 411
 412    /**
 413     * We keep the accessibility events to be processed. If a received event is
 414     * the same type as the previous one it replaces the latter, otherwise it is
 415     * added to the queue. All events in this queue are processed while we speak
 416     * and this occurs after a certain timeout since the last received event.
 417     */
 418    private final EventQueue mEventQueue = new EventQueue();
 419
 420    /**
 421     * Reusable map used for passing parameters to the TextToSpeech.
 422     */
 423    private final HashMap<String, String> mSpeechParametersMap = new HashMap<String, String>();
 424
 425    /**
 426     * Listeners interested in the TalkBack initialization state.
 427     */
 428    private final ArrayList<InfrastructureStateListener> mInfrastructureStateListeners = new ArrayList<InfrastructureStateListener>();
 429
 430    /**
 431     * Flag if a notification is currently spoken.
 432     */
 433    private boolean mSpeakingNotification;
 434
 435    /**
 436     * Runnable to clear mSpeakingNotification to false after the notification has stopped playing.
 437     */
 438    private Runnable mClearSpeakingNotification;
 439
 440    /**
 441     * The TTS engine. Only one of this and mTtsExtended will be non-null at a time.
 442     */
 443    private TextToSpeech mTts = null;
 444
 445    /**
 446     * The TTS engine being initialized.
 447     */
 448    private TextToSpeech mTtsInitializing = null;
 449
 450    /**
 451     * The package name of the default TTS engine.
 452     */
 453    private String defaultTtsEngine = null;
 454
 455    /**
 456     * A handler for switching the TTS engine asynchronously.
 457     */
 458    private Handler mTtsHandler;
 459
 460    /**
 461    * {@link Runnable} used to recover the TTS engine in the event of a media state change.
 462    */
 463    private Runnable mTtsStateRecoverer;
 464
 465    /**
 466     * The TTS extended engine. Only one of this and mTts will be non-null at a
 467     * time.
 468     */
 469    private TextToSpeechBeta mTtsExtended = null;
 470
 471    /**
 472     * The TTS extended engine being initialized.
 473     */
 474    private TextToSpeechBeta mTtsExtendedInitializing = null;
 475
 476    /**
 477     * Proximity sensor for implementing "shut up" functionality.
 478     */
 479    private ProximitySensor mProximitySensor;
 480
 481    /**
 482     * processor for {@link AccessibilityEvent}s that populates
 483     * {@link Utterance}s.
 484     */
 485    private SpeechRuleProcessor mSpeechRuleProcessor;
 486
 487    /**
 488     * Loader of speech rules.
 489     */
 490    private SpeechRuleLoader mSpeechRuleLoader;
 491
 492    /**
 493     * The last event - used to auto-determine the speech queue mode.
 494     */
 495    private int mLastEventType;
 496
 497    /**
 498     * The audio manager used for changing the ringer volume for incoming calls.
 499     */
 500    private AudioManager mAudioManager;
 501
 502    /**
 503     * The activity manager used for determining currently open activities.
 504     */
 505    private ActivityManager mActivityManager;
 506
 507    /**
 508     * The telephony manager used to determine the call state.
 509     */
 510    private TelephonyManager mTelephonyManager;
 511
 512    /**
 513     * The manager for TalkBack plug-ins.
 514     */
 515    private PluginManager mPluginManager;
 516
 517    /**
 518     * {@link BroadcastReceiver} for tracking the ringer mode and screen state.
 519     */
 520    private RingerModeAndScreenMonitor mRingerModeAndScreenMonitor;
 521
 522    /**
 523     * {@link Runnable} for processing the standby of the proximity sensor.
 524     */
 525    private Runnable mProximitySensorSilencer;
 526
 527    /**
 528     * The current ringer mode of the device.
 529     */
 530    private int mRingerMode;
 531
 532    /**
 533     * Flag if the last utterance is uninterruptible.
 534     */
 535    private boolean mLastUtteranceUninterruptible;
 536
 537    /**
 538     * Access to preferences.
 539     */
 540    SharedPreferences mPrefs;
 541
 542    /**
 543     * Whether speech should be silenced based on the ringer mode.
 544     */
 545    private int mRingerPref;
 546
 547    /**
 548     * Whether speech should be silenced based on screen status.
 549     */
 550    private int mScreenPref;
 551
 552    /**
 553     * Whether Caller ID should be spoken.
 554     */
 555    private boolean mCallerIdPref;
 556
 557    /**
 558     * Whether TTS Extended should be used.
 559     */
 560    private boolean mTtsExtendedPref;
 561
 562    /**
 563     * Whether to use the proximity sensor to silence speech.
 564     */
 565    private boolean mProximityPref;
 566
 567    /**
 568     * The version code of the last known running version of TalkBack
 569     */
 570    private int mLastVersion;
 571
 572    /**
 573     * The version code of the currently running version of TalkBack
 574     */
 575    private int mCurVersion;
 576
 577    /**
 578     * Whether the screen is off.
 579     */
 580    private boolean mScreenIsOff;
 581
 582    /**
 583     * When the screen was last turned on.
 584     */
 585    private long mTimeScreenOn;
 586
 587    /**
 588     * The last spoken accessibility event, used for crash reporting.
 589     */
 590    private AccessibilityEvent mLastSpokenEvent = null;
 591
 592    /**
 593     * Flag if the device has a telephony feature, so we know if to initialize
 594     * phone specific stuff.
 595     */
 596    private boolean mDeviceIsPhone;
 597    
 598    /**
 599     * Flag if the device has a touchscreen feature, so we know to initialize
 600     * touchscreen-specific stuff.
 601     */
 602    private boolean mDeviceHasTouchscreen;
 603
 604    /**
 605     * Array of actions to perform when an utterance completes.
 606     */
 607    private ArrayList<UtteranceCompleteAction> mUtteranceCompleteActions
 608            = new ArrayList<UtteranceCompleteAction>();
 609
 610    /**
 611     * The next utterance index; each utterance id will be constructed from this
 612     * ever-increasing index.
 613     */
 614    private int mNextUtteranceIndex = 0;
 615
 616    /**
 617     * Maps the name to command enum for user commands handled by TalkBack.
 618     */
 619    private Map<String, TalkBackCommands> mIdToUserCommands;
 620
 621    /**
 622    * Listener for determining changes in the telephony state.
 623    */
 624    private PhoneStateListener mPhoneStateListener;
 625
 626    /**
 627    * {@link BroadcastReceiver} for determining changes in the media state used
 628    * for switching the TTS engine.
 629    */
 630    private BroadcastReceiver mMediaBroadcastReceiver;
 631    
 632    /**
 633     * Static handle to TalkBack so CommandInterfaceBroadcastReceiver can access
 634     * it.
 635     */
 636    static TalkBackService sInstance;
 637
 638    @Override
 639    public void onCreate() {
 640        super.onCreate();
 641        sInstance = this;
 642    }
 643
 644    @Override
 645    public void onDestroy() {
 646        super.onDestroy();
 647        shutdownInfrastructure();
 648    }
 649
 650    /**
 651     * Returns true if TalkBack is running and initialized.
 652     */
 653    public static boolean isServiceInitialized() {
 654        return sInfrastructureInitialized && sTtsInitialized;
 655    }
 656
 657    /**
 658     * Returns the TalkBackService instance if it's running and initialized,
 659     * otherwise returns null.
 660     */
 661    public static TalkBackService getInstance() {
 662        if (sInfrastructureInitialized) {
 663            return sInstance;
 664        }
 665
 666        return null;
 667    }
 668
 669    /**
 670     * @return The service instance as {@link Context} if it has been
 671     *         instantiated regardless if infrastructure has been initialized;
 672     */
 673    public static Context asContext() {
 674        return sInstance;
 675    }
 676
 677    /**
 678     * Shuts down the infrastructure in case it has been initialized.
 679     */
 680    private void shutdownInfrastructure() {
 681        if (!sInfrastructureInitialized) {
 682            return;
 683        }
 684
 685        if (mProximitySensor != null) {
 686            mProximitySensor.shutdown();
 687        }
 688
 689        mSpeechHandler.obtainMessage(WHAT_SHUTDOWN_TTS).sendToTarget();
 690
 691        if (mRingerModeAndScreenMonitor != null) {
 692            unregisterReceiver(mRingerModeAndScreenMonitor);
 693        }
 694        
 695        if (mPhoneStateListener != null) {
 696            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
 697        }
 698        
 699        if (mMediaBroadcastReceiver != null) {
 700            unregisterReceiver(mMediaBroadcastReceiver);
 701        }
 702
 703        if (mTtsHandler != null && mTtsStateRecoverer != null) {
 704            mTtsHandler.removeCallbacks(mTtsStateRecoverer);
 705        }
 706
 707        sInfrastructureInitialized = false;
 708        notifyInfrastructureStateListeners();
 709        mInfrastructureStateListeners.clear();
 710    }
 711
 712    @Override
 713    public void onServiceConnected() {
 714        shutdownInfrastructure();
 715
 716        setServiceInfo();
 717        initializeInfrastructure();
 718
 719        if (mDeviceIsPhone) {
 720            tryShowSettingsManagerAvailable();
 721            registerUncaughtExceptionHandler();
 722            processCrashLog();
 723        }
 724    }
 725
 726    /**
 727     * Sets the {@link AccessibilityService} for configuring how the system
 728     * handles TalkBack.
 729     */
 730    public void setServiceInfo() {
 731        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
 732        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
 733        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
 734        info.notificationTimeout = 0;
 735        info.flags = AccessibilityServiceInfo.DEFAULT;
 736        setServiceInfo(info);
 737    }
 738
 739    /**
 740     * Initializes the infrastructure.
 741     */
 742    private void initializeInfrastructure() {
 743        // first check if we are running on a phone
 744        mDeviceIsPhone = getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
 745
 746        // check if the device has a touchscreen
 747        mDeviceHasTouchscreen = getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
 748
 749        // start the TTS service
 750        mSpeechHandler.obtainMessage(WHAT_START_TTS).sendToTarget();
 751
 752        // create a speech processor for generating utterances
 753        mSpeechRuleProcessor = new SpeechRuleProcessor(this);
 754
 755        // initialize the speech rule loader and load speech rules
 756        mSpeechRuleLoader = new SpeechRuleLoader(getPackageName(), mSpeechRuleProcessor,
 757                mDeviceIsPhone);
 758        mSpeechRuleLoader.loadSpeechRules();
 759        addInfrastructureStateListener(mSpeechRuleLoader);
 760        
 761        // We initialize phone specific stuff only if needed
 762        if (mDeviceIsPhone) {
 763            // Create and register in a proximity sensor for stopping speech
 764            initializeProximitySensor();
 765            
 766            // get the AudioManager and configure according the current ring mode
 767            mAudioManager = (AudioManager) getSystemService(Service.AUDIO_SERVICE);
 768
 769            // get the ringer mode on start
 770            mRingerMode = mAudioManager.getRingerMode();
 771
 772            // get the TelephonyManager
 773            mTelephonyManager = (TelephonyManager) getSystemService(Service.TELEPHONY_SERVICE);
 774
 775            // TODO(svetoslavganov): For now preferences are supported only on devices
 776            //   with telephony feature i.e. phones
 777
 778            // load preferences
 779            mPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
 780            reloadPreferences();
 781
 782            // write preferences in case we set anything to its default value for the first time.
 783            SharedPreferences.Editor prefsEditor = mPrefs.edit();
 784            prefsEditor.putString(getString(R.string.pref_speak_ringer_key), "" + mRingerPref);
 785            prefsEditor.putString(getString(R.string.pref_speak_screenoff_key), "" + mScreenPref);
 786            prefsEditor.putBoolean(getString(R.string.pref_caller_id_key), mCallerIdPref);
 787            prefsEditor.putBoolean(getString(R.string.pref_tts_extended_key), mTtsExtendedPref);
 788            prefsEditor.putBoolean(getString(R.string.pref_proximity_key), mProximityPref);
 789            prefsEditor.commit();
 790            
 791            // register a phone state listener for the connectivity command.
 792            mPhoneStateListener = new SavingPhoneStateListener(this);
 793            mTelephonyManager.listen(mPhoneStateListener, 
 794                    PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE);
 795        }
 796        
 797        if (mDeviceIsPhone || mDeviceHasTouchscreen) {
 798            /*
 799             * Although this receiver includes code responding to phone-specific
 800             * intents, it should also be registered for touch screen devices
 801             * without telephony.
 802             */
 803            mRingerModeAndScreenMonitor = new RingerModeAndScreenMonitor();
 804            mRingerModeAndScreenMonitor.register(this);
 805        }
 806
 807        // get the ActivityManager
 808        mActivityManager = (ActivityManager) getSystemService(Service.ACTIVITY_SERVICE);
 809
 810        // instantiate the plug-in manager and load plug-ins
 811        mPluginManager = new PluginManager(this, mSpeechRuleProcessor);
 812        addInfrastructureStateListener(mPluginManager);
 813
 814        mScreenIsOff = false;
 815        mTimeScreenOn = SystemClock.uptimeMillis();
 816
 817        mSpeakingNotification = false;
 818        mClearSpeakingNotification = new Runnable() {
 819            public void run() {
 820                mSpeakingNotification = false;
 821            }
 822        };
 823
 824        // register the class loading manager
 825        addInfrastructureStateListener(ClassLoadingManager.getInstance());
 826
 827        // add a listener to respond to sdcard
 828        IntentFilter mediaIntentFilter = new IntentFilter();
 829        mediaIntentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
 830        mediaIntentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
 831        mediaIntentFilter.addDataScheme("file");
 832        mTtsHandler = new Handler();
 833        mMediaBroadcastReceiver = new BroadcastReceiver() {
 834            @Override
 835            public void onReceive(Context context, Intent intent) {
 836                String action = intent.getAction();
 837                if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
 838                    if (defaultTtsEngine != null) {
 839                        mTtsStateRecoverer = new Runnable() {
 840                            @Override
 841                            public void run() {
 842                                setTtsEngineKeepTrying(defaultTtsEngine, 3000, 20);
 843                            }
 844                        };
 845                        mTtsHandler.postDelayed(mTtsStateRecoverer, 5000);
 846                    }
 847                } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
 848                    mTtsStateRecoverer = new Runnable() {
 849                        @Override
 850                        public void run() {
 851                            setTtsEngineKeepTrying("com.svox.pico", 3000, 20);
 852                        }
 853                    };
 854                    mTtsHandler.postDelayed(mTtsStateRecoverer, 5000);
 855                }
 856            }
 857        };
 858        registerReceiver(mMediaBroadcastReceiver, mediaIntentFilter);
 859
 860        initializeCommands();
 861
 862        sInfrastructureInitialized = true;
 863        notifyInfrastructureStateListeners();
 864    }
 865
 866
 867    /**
 868     * Try to switch the TTS engine, repeating if necessary.
 869     *
 870     * @param engine The package name of the desired TTS engine
 871     * @param retryInterval How often to retry switching, in milliseconds
 872     * @param maxTries How many times to try switching before giving up
 873     */
 874    private void setTtsEngineKeepTrying(
 875            final String engine, final int retryInterval, final int maxTries) {
 876        if (maxTries < 1 || mTts == null || TextToSpeech_setEngineByPackageName == null) {
 877            return;
 878        }
 879        int result = setEngineByPackageName(mTts, engine);
 880        if (result == TextToSpeech.ERROR) {
 881            mTtsHandler.postDelayed(new Runnable() {
 882                @Override
 883                public void run() {
 884                    setTtsEngineKeepTrying(engine, retryInterval, maxTries - 1);
 885                }
 886            }, retryInterval);
 887        }
 888    }
 889
 890    private void initializeCommands() {
 891        CommandsManager accessor = new CommandsManager();
 892        accessor.addCommands(
 893                Arrays.<Command>asList(TalkBackCommands.values()),
 894                getApplicationContext());
 895        mIdToUserCommands = new HashMap<String, TalkBackCommands>();
 896
 897        for (TalkBackCommands command : TalkBackCommands.values()) {
 898            mIdToUserCommands.put(command.getDisplayName(), command);
 899        }
 900    }
 901
 902    private void initializeProximitySensor() {
 903        mProximitySensor = new ProximitySensor(this, true, new ProximityChangeListener() {
 904            @Override
 905            public void onProximityChanged(float proximity) {
 906                if (proximity == 0) {
 907                    // Stop all speech if the user is touching the proximity
 908                    // sensor
 909                    if (mTts != null || mTtsExtended != null) {
 910                        mSpeechHandler.obtainMessage(WHAT_STOP_ALL_SPEAKING).sendToTarget();
 911                    }
 912                }
 913            }
 914        });
 915
 916        // Create a Runnable for causing the standby of the proximity sensor.
 917        mProximitySensorSilencer = new Runnable() {
 918            @Override
 919            public void run() {
 920                if (mProximitySensor != null) {
 921                    mProximitySensor.standby();
 922                }
 923            }
 924        };
 925    }
 926
 927    /**
 928     * Adds an {@link InfrastructureStateListener}.
 929     */
 930    public void addInfrastructureStateListener(InfrastructureStateListener listener) {
 931        mInfrastructureStateListeners.add(listener);
 932    }
 933
 934    /**
 935     * Removes an {@link InfrastructureStateListener}.
 936     */
 937    public void removeInfrastructureStateListener(InfrastructureStateListener listener) {
 938        mInfrastructureStateListeners.remove(listener);
 939    }
 940
 941
 942     /**
 943      * Version 12 (2.4.0) introduced a preference for tracking the last run
 944      * version of TalkBack. This method processes one-time tasks defined to run
 945      * after upgrades or installations from a specific version.
 946      */
 947     private void tryShowSettingsManagerAvailable() {
 948         // this is method is a NOOP on non-phone devices
 949
 950         if (!mDeviceIsPhone) {
 951             return;
 952         }
 953
 954         // Obtain the current and previous version numbers
 955         mLastVersion = mPrefs.getInt(getString(R.string.pref_last_talkback_version), 0);
 956         PackageManager packageManager = getPackageManager();
 957         try {
 958             mCurVersion = packageManager.getPackageInfo(getPackageName(), 0).versionCode;
 959         } catch (NameNotFoundException e) {
 960             // TalkBack couldn't locate it's own PackageInfo, probably not a
 961             // good thing.
 962             mCurVersion = 0;
 963         }
 964         if (mLastVersion == 0) {
 965             // Display a notification about the settings activity only if
 966             // the user is running version 12 or higher and did a clean install
 967             // or upgraded from a version earlier than 12 and the application is
 968             // not yet already installed.
 969             Intent settingsManager = new Intent(Intent.ACTION_MAIN);
 970             settingsManager.setPackage(SETTINGS_MANAGER_PACKAGE);
 971             List<ResolveInfo> settingsManagerPresence = packageManager.queryIntentActivities(
 972                     settingsManager, 0);
 973             if (settingsManagerPresence == null || settingsManagerPresence.isEmpty()) {
 974                 displaySettingsAvailableNotification();
 975             }
 976         } else {
 977             // We want to track the number of times that TalkBack has started
 978             // without notifying the user about the new settings activity. This
 979             // will be used later to tune further announcements.
 980             int notificationlessLaunches = mPrefs.getInt(
 981                     getString(R.string.pref_notificationless_launches), 0);
 982             SharedPreferences.Editor editor = mPrefs.edit();
 983             editor.putInt(getString(R.string.pref_notificationless_launches),
 984                     notificationlessLaunches + 1);
 985             editor.commit();
 986         }
 987         if (mLastVersion != mCurVersion) {
 988             SharedPreferences.Editor editor = mPrefs.edit();
 989             editor.putInt(getString(R.string.pref_last_talkback_version), mCurVersion);
 990             editor.commit();
 991         }
 992     }
 993
 994
 995     /**
 996      * Displays a notification that TalkBack now offers customizable settings by
 997      * downloading the Accessibility Settings Manager application.
 998      */
 999    private void displaySettingsAvailableNotification() {
1000        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
1001        Intent launchMarketIntent = new Intent(Intent.ACTION_VIEW);
1002        launchMarketIntent.setData(Uri.parse(getString(R.string.settings_manager_market_uri)));
1003        Notification notification = new Notification(android.R.drawable.stat_notify_more,
1004
1005                getString(R.string.title_talkback_settings_available), System.currentTimeMillis());
1006
1007        notification.setLatestEventInfo(this,
1008                getString(R.string.title_talkback_settings_available),
1009                getString(R.string.message_talkback_settings_available), PendingIntent.getActivity(
1010                        this, 0, launchMarketIntent, PendingIntent.FLAG_UPDATE_CURRENT));
1011        notification.defaults |= Notification.DEFAULT_SOUND;
1012        notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_ONGOING_EVENT;
1013        nm.notify(SETTINGS_NOTIFICATION_ID, notification);
1014    }
1015
1016    /**
1017     * Displays a notification that TalkBack has crashed and data can be sent to
1018     * developers to help improve TalkBack.
1019     */
1020    private void displayCrashReportNotification(String crashReport) {
1021        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
1022        Intent emailIntent = new Intent(Intent.ACTION_SEND);
1023        emailIntent.setType("plain/text");
1024        emailIntent.putExtra(Intent.EXTRA_EMAIL, CRASH_REPORT_EMAILS);
1025        emailIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.subject_crash_report_email));
1026        emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
1027                getString(R.string.header_crash_report_email) + crashReport);
1028        Notification notification = new Notification(android.R.drawable.stat_notify_error,
1029                getString(R.string.title_talkback_crash), System.currentTimeMillis());
1030
1031        notification.setLatestEventInfo(this,
1032                getString(R.string.title_talkback_crash),
1033                getString(R.string.message_talkback_crash), PendingIntent.getActivity(
1034                        this, 0, emailIntent, PendingIntent.FLAG_UPDATE_CURRENT));
1035        notification.defaults |= Notification.DEFAULT_SOUND;
1036        notification.flags |= Notification.FLAG_AUTO_CANCEL;
1037        nm.notify(CRASH_NOTIFICATION_ID, notification);
1038    }
1039
1040    /**
1041     * Notifies the {@link InfrastructureStateListener}s.
1042     */
1043    private void notifyInfrastructureStateListeners() {
1044        ArrayList<InfrastructureStateListener> listeners = mInfrastructureStateListeners;
1045        for (int i = 0, count = listeners.size(); i < count; i++) {
1046            InfrastructureStateListener listener = listeners.get(i);
1047            listener.onInfrastructureStateChange(sInfrastructureInitialized);
1048        }
1049    }
1050
1051    /**
1052     * Registers an uncaught exception handler for TalkBack. Causes TalkBack to
1053     * store crash data before the thread terminates.
1054     */
1055    private void registerUncaughtExceptionHandler() {
1056        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
1057            private UncaughtExceptionHandler sysUeh = Thread.getDefaultUncaughtExceptionHandler();
1058
1059            @Override
1060            public void uncaughtException(Thread thread, Throwable ex) {
1061                String timestamp = new Date().toString();
1062                String androidOsVersion = Build.VERSION.SDK_INT + " - " + Build.VERSION.INCREMENTAL;
1063                String deviceInfo = Build.MANUFACTURER + ", " + Build.MODEL + ", " + Build.PRODUCT;
1064                String talkBackVersion = "" + mCurVersion;
1065                StringBuilder stackTrace = new StringBuilder();
1066                stackTrace.append(ex.toString() + "\n");
1067                for (StackTraceElement element : ex.getStackTrace()) {
1068                    stackTrace.append(element.toString() + "\n");
1069                }
1070                String lastEvent = mLastSpokenEvent != null ? mLastSpokenEvent.toString() : "null";
1071                writeCrashReport(
1072                        String.format(getString(R.string.template_crash_report_message), timestamp,
1073                                androidOsVersion, deviceInfo, talkBackVersion, stackTrace,
1074                                lastEvent));
1075                // Show the standard "Force Close" alert dialog.
1076                if (sysUeh != null) {
1077                    sysUeh.uncaughtException(thread, ex);
1078                }
1079            }
1080
1081            private void writeCrashReport(String report) {
1082                FileOutputStream fos;
1083                try {
1084                    fos = openFileOutput(TALKBACK_CRASH_LOG, Context.MODE_APPEND);
1085                    fos.write(report.getBytes());
1086                    fos.flush();
1087                    fos.close();
1088                } catch (FileNotFoundException e) {
1089                    // Ignored
1090                } catch (IOException e) {
1091                    // Ignored
1092                }
1093            }
1094        });
1095    }
1096
1097    /**
1098     * Checks the TalkBack crash log and prompts the user to submit reports if appropriate.
1099     */
1100    private void processCrashLog() {
1101        StringBuilder crashReport = new StringBuilder();
1102        String line = null;
1103        try {
1104            BufferedReader br = new BufferedReader(
1105                    new FileReader(getFilesDir() + "/" + TALKBACK_CRASH_LOG));
1106            while ((line = br.readLine()) != null) {
1107                crashReport.append(line + "\n");
1108            }
1109        } catch (FileNotFoundException e) {
1110            // Handles the case where no crash log exists.
1111            return;
1112        } catch (IOException e) {
1113            // Don't bother the user with this.
1114            return;
1115        }
1116        deleteFile(TALKBACK_CRASH_LOG);
1117        displayCrashReportNotification(crashReport.toString());
1118    }
1119
1120    /**
1121     * Reload the preferences from the SharedPreferences object.
1122     */
1123    public void reloadPreferences() {
1124        // This method is a NOOP on non-phone devices
1125        if (!mDeviceIsPhone) {
1126            return;
1127        }
1128
1129        mRingerPref = Integer.parseInt(mPrefs.getString(
1130                getString(R.string.pref_speak_ringer_key), "" + PREF_RINGER_DEFAULT));
1131        mScreenPref = Integer.parseInt(mPrefs.getString(
1132                getString(R.string.pref_speak_screenoff_key), "" + PREF_SCREEN_DEFAULT));
1133        mCallerIdPref = mPrefs.getBoolean(
1134                getString(R.string.pref_caller_id_key), PREF_CALLER_ID_DEFAULT);
1135        mTtsExtendedPref = mPrefs.getBoolean(
1136                getString(R.string.pref_tts_extended_key), PREF_TTS_EXTENDED_DEFAULT);
1137        mProximityPref = mPrefs.getBoolean(
1138                getString(R.string.pref_proximity_key), PREF_PROXIMITY_DEFAULT);
1139
1140        // Switch TTS engines if necessary.
1141        if (sInfrastructureInitialized) {
1142            if ((mTtsExtendedPref && mTts != null) ||
1143                (!mTtsExtendedPref && mTtsExtended != null)) {
1144                mSpeechHandler.obtainMessage(WHAT_SWITCH_TTS).sendToTarget();
1145            }
1146        }
1147
1148        // Power off the proximity sensor if necessary.
1149        if (mProximitySensor != null) {
1150            if (!mProximityPref && mProximitySensor.getState() != ProximitySensor.STATE_STOPPED) {
1151                mProximitySensor.shutdown();
1152                // After calling shutdown, the instance loses state and must be
1153                // discarded.
1154                mProximitySensor = null;
1155            }
1156        }
1157
1158        // Power on the proximity sensor if necessary.
1159        if (mProximityPref) {
1160            if (mProximitySensor == null
1161                    || mProximitySensor.getState() == ProximitySensor.STATE_STOPPED) {
1162                initializeProximitySensor();
1163            }
1164        }
1165    }
1166
1167    @Override
1168    public void onAccessibilityEvent(AccessibilityEvent event) {
1169        if (event == null) {
1170            Log.e(LOG_TAG, "Received null accessibility event.");
1171            return;
1172        }
1173
1174        if (!isServiceInitialized()) {
1175            Log.w(LOG_TAG, "No TTS instance found - dropping event.");
1176            return;
1177        }
1178
1179        if (mDeviceIsPhone) {
1180            // Check ringer state
1181            if (mRingerPref == PREF_RINGER_NOT_SILENT_OR_VIBRATE
1182                && (mRingerMode == AudioManager.RINGER_MODE_VIBRATE
1183                    || mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
1184                return;
1185            } else if (mRingerPref == PREF_RINGER_NOT_SILENT &&
1186                       mRingerMode == AudioManager.RINGER_MODE_SILENT) {
1187                return;
1188            }
1189
1190            // Check screen state; screen off can be silenced, but caller ID can override.
1191            boolean silence = false;
1192            if (mScreenPref == PREF_SCREEN_OFF_DISALLOWED && mScreenIsOff) {
1193                silence = true;
1194                if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
1195                    String text = Utils.getEventText(this, event).toString();
1196
1197                    NotificationType type;
1198                    Parcelable parcelable = event.getParcelableData();
1199                    if (parcelable instanceof Notification) {
1200                        int icon = ((Notification) parcelable).icon;
1201                        // don't record phone calls in the cache because we get missed call anyway
1202                        if (icon == NotificationType.ICON_PHONE_CALL) {
1203                            type = null; // ignore this notification
1204                        } else {
1205                            type = NotificationType.getNotificationTypeFromIcon(icon);
1206                        }
1207                    } else {
1208                        type = null;
1209                    }
1210
1211                    updateNotificationCache(type, text);
1212                }
1213            }
1214
1215            // If the state is ringing, then the Caller ID pref overrides what we do.
1216            if (mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_RINGING) {
1217                silence = (mCallerIdPref == false);
1218            }
1219
1220            if (silence) {
1221                return;
1222            }
1223        }
1224
1225        // avoid processing duplicate events
1226        if (equals(mLastSpokenEvent, event)) {
1227            Log.i(LOG_TAG, "duplicate");
1228            return;
1229        }
1230
1231        // Keep a representation of the last spoken event for crash reporting.
1232        mLastSpokenEvent = clone(event);
1233
1234        synchronized (mEventQueue) {
1235            enqueueEventLocked(event);
1236            if (isSourceInCallScreenActivity(event)) {
1237                sendSpeakMessageLocked(WHAT_SPEAK_WHILE_IN_CALL, EVENT_TIMEOUT_IN_CALL_SCREEN);
1238            } else {
1239                sendSpeakMessageLocked(WHAT_SPEAK, EVENT_TIMEOUT);
1240            }
1241        }
1242
1243        return;
1244    }
1245
1246    public AccessibilityEvent getLastSpokenEvent() {
1247        return clone(mLastSpokenEvent);
1248    }
1249
1250    /**
1251     * Returns if the <code>event</code> source is the in-call screen activity.
1252     */
1253    private boolean isSourceInCallScreenActivity(AccessibilityEvent event) {
1254        return (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()
1255                && CLASS_NAME_IN_CALL_SCREEN.equals(event.getClassName()));
1256    }
1257
1258    @Override
1259    public void onInterrupt() {
1260        mSpeechHandler.obtainMessage(WHAT_STOP_ALL_SPEAKING).sendToTarget();
1261    }
1262
1263    /**
1264     * Enqueues the an <code>event</code>. The queuing operates as follows: </p>
1265     * 1. Events within the event timeout with type same as the last event
1266     * replace the latter if they are not notification with different icon.
1267     * </br> 2. All other events are enqueued.
1268     *
1269     * @param event The event to enqueue.
1270     */
1271    private void enqueueEventLocked(AccessibilityEvent event) {
1272        AccessibilityEvent current = clone(event);
1273        ArrayList<AccessibilityEvent> eventQueue = mEventQueue;
1274
1275        int lastIndex = eventQueue.size() - 1;
1276        if (lastIndex > -1) {
1277            AccessibilityEvent last = eventQueue.get(lastIndex);
1278            if (isSameEventTypeAndSameNotificationIconAndTickerText(event, last)) {
1279                // in this special case we want to keep the first event
1280                // since the system is adding hyphens to the dialed number
1281                // which generates events we want to disregard
1282                if (isFromDialerInput(event)) {
1283                    return;
1284                }
1285                eventQueue.clear();
1286            }
1287        }
1288
1289        eventQueue.add(current);
1290    }
1291
1292    /**
1293     * Returns if the <code>currentEvent</code> has different type from the
1294     * <code>lastEvent</code> or if they are
1295     * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} if the
1296     * {@link Notification} instances they carry do not have the same icon and
1297     * ticker text.
1298     */
1299    private boolean isSameEventTypeAndSameNotificationIconAndTickerText(
1300            AccessibilityEvent currentEvent, AccessibilityEvent lastEvent) {
1301        if (currentEvent.getEventType() != lastEvent.getEventType()) {
1302            return false;
1303        }
1304
1305        if (currentEvent.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
1306            return true;
1307        }
1308
1309        Notification currentNotification = (Notification) currentEvent.getParcelableData();
1310        Notification lastNotification = (Notification) lastEvent.getParcelableData();
1311
1312        if (currentNotification == null) {
1313            if (lastNotification != null) {
1314                return false;
1315            }
1316            return true;
1317        } else if (lastNotification == null) {
1318            return false;
1319        }
1320
1321        if (currentNotification.icon != lastNotification.icon) {
1322            return false;
1323        }
1324
1325        if (currentNotification.tickerText == null) {
1326            if (lastNotification.tickerText != null) {
1327                return false;
1328            }
1329            return true;
1330        } else {
1331            return currentNotification.tickerText.equals(lastNotification.tickerText);
1332        }
1333    }
1334
1335    /**
1336     * Returns if a given <code>event</code> is fired by the dialer input which
1337     * contains the currently dialed number. </p> Note: The Android framework
1338     * adds hyphens between the dialed number digits which fires accessibility
1339     * events. Since TalkBackService processes only the last event of a given
1340     * type in a given time frame the original event is replaced by a more
1341     * recent hyphen adding event.
1342     *
1343     * @param event The event we are checking.
1344     * @return True if the event comes from the dialer input box, false
1345     *         ohterwise.
1346     */
1347    private boolean isFromDialerInput(AccessibilityEvent event) {
1348        return (PACKAGE_NAME_CONTACTS.equals(event.getPackageName()) && EditText.class
1349                .getCanonicalName().equals(event.getClassName()));
1350    }
1351
1352    /**
1353     * Sends {@link #WHAT_SPEAK} to the speech handler. This method cancels the
1354     * old message (if such exists) since it is no longer relevant.
1355     *
1356     * @param action The action to perform with the message.
1357     * @param timeout The timeout after which to send the message.
1358     */
1359    public void sendSpeakMessageLocked(int action, long timeout) {
1360        Handler handler = mSpeechHandler;
1361        handler.removeMessages(action);
1362        Message message = handler.obtainMessage(action);
1363        handler.sendMessageDelayed(message, timeout);
1364    }
1365
1366    /**
1367     * Processes an <code>event</code> by asking the {@link SpeechRuleProcessor}
1368     * to match it against its rules and in case an utterance is generated it is
1369     * spoken. This method is responsible for recycling of the processed event.
1370     *
1371     * @param event The event to process.
1372     * @param queueMode The queuing mode to use while processing events.
1373     * @param action The action to perform with the message.
1374     */
1375    private void processAndRecycleEvent(AccessibilityEvent event, int queueMode, int action) {
1376        String currentActivity = "";
1377        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
1378        if (tasks != null && tasks.size() >= 1) {
1379            currentActivity = tasks.get(0).topActivity.getClassName();
1380        }
1381
1382        Log.d(LOG_TAG, "Processing event: " + event + " activity=" + currentActivity);
1383
1384        Utterance utterance = Utterance.obtain();
1385        // For now we do not pass any filter/formatter arguments. Their purpose is to
1386        // ensure future flexibility.
1387        if (mSpeechRuleProcessor.processEvent(event, currentActivity, utterance, null, null)) {
1388            HashMap<String, Object> metadata = utterance.getMetadata();
1389            // notifications are never interruptible
1390            boolean speakingNotif

Large files files are truncated, but you can click here to view the full file