@Override
  protected void onResume() {
    super.onResume();
    sTutorialIsActive = true;

    /*
     * Handle the cases where the tutorial was started with TalkBack in an
     * invalid state (inactive, suspended, or without Explore by Touch
     * enabled).
     */
    final ServiceState serviceState = TalkBackService.getServiceState();
    /*
     * Check for suspended state first because touch exploration reports it
     * is disabled when TalkBack is suspended.
     */
    if (serviceState == ServiceState.SUSPENDED) {
      showAlertDialogAndFinish(
          R.string.accessibility_tutorial_service_suspended_title,
          R.string.accessibility_tutorial_service_suspended_message);
      return;
    } else if ((serviceState == ServiceState.INACTIVE)
        || !mAccessibilityManager.isTouchExplorationEnabled()) {
      showAlertDialogAndFinish(
          R.string.accessibility_tutorial_service_inactive_title,
          R.string.accessibility_tutorial_service_inactive_message);
      return;
    }

    final TalkBackService service = TalkBackService.getInstance();
    service.addServiceStateListener(mServiceStateListener);

    if (mFirstTimeResume) {
      mFirstTimeResume = false;

      if (mSavedInstanceState != null) {
        show(mSavedInstanceState.getInt(KEY_ACTIVE_MODULE, DEFAULT_MODULE));
      } else {
        show(DEFAULT_MODULE);
      }
    }

    getCurrentModule().onResume();

    if (mResourceIdToRepeat > 0) {
      mRepeatHandler.sendEmptyMessageDelayed(RepeatHandler.MSG_REPEAT, RESUME_REPEAT_DELAY);
    }
  }
        @Override
        public void onServiceStateChanged(ServiceState newState) {
          if (newState == ServiceState.INACTIVE) {
            // If the service dies while the tutorial is active, exit.
            finish();
          } else if (newState == ServiceState.SUSPENDED) {
            stopRepeating();

            // If the service is suspended, show an alert and exit.
            showAlertDialogAndFinish(
                R.string.accessibility_tutorial_service_suspended_title,
                R.string.accessibility_tutorial_service_suspended_message);
          }
        }
  /**
   * Determines the shortcut gesture direction for performing the given action based on user
   * preferences, raising an alert and exiting the tutorial if there is no corresponding gesture.
   *
   * @param action The action for which to find a gesture direction.
   * @return A resource ID of the user-facing String describing the direction of a swipe gesture
   *     corresponding to the requested action. If multiple gestures correspond to the action, only
   *     one of them will be returned. If there is no gesture for the action, a negative value will
   *     be returned.
   */
  protected int getGestureDirectionForRequiredAction(ShortcutGestureAction action) {
    final AccessibilityTutorialActivity parentActivity = getParentTutorial();
    final int direction = GesturePreferenceUtils.getDirectionForAction(parentActivity, action);

    if (direction < 0) {
      parentActivity.stopRepeating();

      final String title =
          parentActivity.getString(R.string.accessibility_tutorial_missing_assignment_title);
      final String actionLabel = action.getLabel(parentActivity);
      final String message =
          parentActivity.getString(
              R.string.accessibility_tutorial_missing_assignment_message, actionLabel);

      parentActivity.showAlertDialogAndFinish(title, message);
    }

    return direction;
  }
 protected void showAlertDialogAndFinish(int titleId, int messageId) {
   showAlertDialogAndFinish(getString(titleId), getString(messageId));
 }