package com.appboy.ui; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import com.appboy.Appboy; import com.appboy.support.StringUtils; import com.appboy.support.ValidationUtils; public class AppboyFeedbackFragment extends Fragment { /** * Listener called in response to feedback lifecycle events. */ public interface FeedbackFinishedListener { /** * Called when the user finishes the feedback fragment by submitting feedback or cancelling. * * @param feedbackResult */ void onFeedbackFinished(FeedbackResult feedbackResult); /** * Called just before a user-submitted feedback message is sent to Appboy. * <p/> * Allows modification or augmentation of the message before it is sent to Appboy. * * @param message the feedback message as written by the user * @return the feedback message that will be sent to Appboy */ String beforeFeedbackSubmitted(String message); } public enum FeedbackResult { SENT, CANCELLED, ERROR } private Button mCancelButton; private Button mSendButton; private CheckBox mIsBugCheckBox; private EditText mMessageEditText; private EditText mEmailEditText; private TextWatcher mSendButtonWatcher; private View.OnClickListener mCancelListener; private View.OnClickListener mSendListener; private FeedbackFinishedListener mFeedbackFinishedListener; private int mOriginalSoftInputMode; private boolean mErrorMessageShown; public AppboyFeedbackFragment() { } @Override public void onAttach(Activity activity) { super.onAttach(activity); mSendButtonWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence sequence, int start, int count, int after) { } @Override public void onTextChanged(CharSequence sequence, int start, int before, int count) { } @Override public void afterTextChanged(Editable sequence) { if (mErrorMessageShown) { // Only show error messages after the user has clicked the send button at least once. ensureSendButton(); } } }; mCancelListener = new View.OnClickListener() { @Override public void onClick(View view) { hideSoftKeyboard(); if (mFeedbackFinishedListener != null) { mFeedbackFinishedListener.onFeedbackFinished(FeedbackResult.CANCELLED); } clearData(); } }; mSendListener = new View.OnClickListener() { @Override public void onClick(View view) { if (ensureSendButton()) { hideSoftKeyboard(); boolean isBug = mIsBugCheckBox.isChecked(); String message = mMessageEditText.getText().toString(); String email = mEmailEditText.getText().toString(); if (mFeedbackFinishedListener != null) { message = mFeedbackFinishedListener.beforeFeedbackSubmitted(message); } boolean result = Appboy.getInstance(getActivity()).submitFeedback(email, message, isBug); if (mFeedbackFinishedListener != null) { mFeedbackFinishedListener.onFeedbackFinished(result ? FeedbackResult.SENT : FeedbackResult.ERROR); } clearData(); } else { mErrorMessageShown = true; } } }; setRetainInstance(true); } @Override public View onCreateView(LayoutInflater layoutInflater, ViewGroup container, Bundle savedInstanceState) { View view = layoutInflater.inflate(R.layout.com_appboy_feedback, container, false); mCancelButton = (Button) view.findViewById(R.id.com_appboy_feedback_cancel); mSendButton = (Button) view.findViewById(R.id.com_appboy_feedback_send); mIsBugCheckBox = (CheckBox) view.findViewById(R.id.com_appboy_feedback_is_bug); mMessageEditText = (EditText) view.findViewById(R.id.com_appboy_feedback_message); mEmailEditText = (EditText) view.findViewById(R.id.com_appboy_feedback_email); mMessageEditText.addTextChangedListener(mSendButtonWatcher); mEmailEditText.addTextChangedListener(mSendButtonWatcher); mCancelButton.setOnClickListener(mCancelListener); mSendButton.setOnClickListener(mSendListener); return view; } @Override public void onResume() { super.onResume(); Appboy.getInstance(getActivity()).logFeedbackDisplayed(); Activity activity = getActivity(); Window window = activity.getWindow(); // Overriding the soft input mode of the Window so that the Send and Cancel buttons appear above // the soft keyboard when either EditText field gains focus. We cache the mode in order to set it // back to the original value when the Fragment is paused. mOriginalSoftInputMode = window.getAttributes().softInputMode; window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); Appboy.getInstance(activity).logFeedbackDisplayed(); } @Override public void onDestroyView() { super.onDestroyView(); mMessageEditText.removeTextChangedListener(mSendButtonWatcher); mEmailEditText.removeTextChangedListener(mSendButtonWatcher); } public void setFeedbackFinishedListener(FeedbackFinishedListener feedbackFinishedListener) { mFeedbackFinishedListener = feedbackFinishedListener; } private boolean validatedMessage() { boolean validMessage = mMessageEditText.getText() != null && !StringUtils.isNullOrBlank(mMessageEditText.getText().toString()); if (validMessage) { mMessageEditText.setError(null); } else { // Display error message in the message box mMessageEditText.setError(getResources().getString(R.string.com_appboy_feedback_form_invalid_message)); } return validMessage; } private boolean validatedEmail() { boolean validEmail = mEmailEditText.getText() != null && !StringUtils.isNullOrBlank(mEmailEditText.getText().toString()) && ValidationUtils.isValidEmailAddress(mEmailEditText.getText().toString()); boolean blankEmail = mEmailEditText.getText() != null && StringUtils.isNullOrBlank(mEmailEditText.getText().toString()); if (validEmail) { mEmailEditText.setError(null); } else if (blankEmail) { // Display blank email error message in the email box mEmailEditText.setError(getResources().getString(R.string.com_appboy_feedback_form_empty_email)); } else { // Display general invalid email error message in the email box mEmailEditText.setError(getResources().getString(R.string.com_appboy_feedback_form_invalid_email)); } return validEmail; } private boolean ensureSendButton() { // Both validators should run, so don't short-circuit this AND statement. return (validatedMessage() & validatedEmail()); } private void clearData() { mEmailEditText.setText(""); mMessageEditText.setText(""); mIsBugCheckBox.setChecked(false); mErrorMessageShown = false; mEmailEditText.setError(null); mMessageEditText.setError(null); } private void hideSoftKeyboard() { Activity activity = getActivity(); activity.getWindow().setSoftInputMode(mOriginalSoftInputMode); // Hide keyboard when paused. View currentFocusView = activity.getCurrentFocus(); if (currentFocusView != null) { InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN); } } }