package net.hockeyapp.android; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcelable; import android.text.TextUtils; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import net.hockeyapp.android.adapters.MessagesAdapter; import net.hockeyapp.android.objects.ErrorObject; import net.hockeyapp.android.objects.FeedbackMessage; import net.hockeyapp.android.objects.FeedbackResponse; import net.hockeyapp.android.objects.FeedbackUserDataElement; import net.hockeyapp.android.tasks.ParseFeedbackTask; import net.hockeyapp.android.tasks.SendFeedbackTask; import net.hockeyapp.android.utils.AsyncTaskUtils; import net.hockeyapp.android.utils.HockeyLog; import net.hockeyapp.android.utils.PrefsUtil; import net.hockeyapp.android.utils.Util; import net.hockeyapp.android.views.AttachmentListView; import net.hockeyapp.android.views.AttachmentView; import java.lang.ref.WeakReference; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; /** * <h3>Description</h3> * * Activity to show the feedback form. **/ public class FeedbackActivity extends Activity implements OnClickListener { /** * The URL of the feedback endpoint for this app. */ public static final String EXTRA_URL = "url"; /** * Optional extra that can be passed as {@code true} to force a new feedback message thread. */ public static final String EXTRA_FORCE_NEW_THREAD = "forceNewThread"; /** * Extra for initial username to set for the feedback message. */ public static final String EXTRA_INITIAL_USER_NAME = "initialUserName"; /** * Extra for initial email address to set for the feedback message. */ public static final String EXTRA_INITIAL_USER_EMAIL = "initialUserEmail"; /** * Extra for any initial attachments to add to the feedback message. */ public static final String EXTRA_INITIAL_ATTACHMENTS = "initialAttachments"; /** * Number of attachments allowed per message. **/ private static final int MAX_ATTACHMENTS_PER_MSG = 3; /** * ID of error dialog **/ private static final int DIALOG_ERROR_ID = 0; /** * Activity request constants for ContextMenu and Chooser Intent */ private static final int ATTACH_PICTURE = 1; private static final int ATTACH_FILE = 2; private static final int PAINT_IMAGE = 3; /** * Initial user's name to pre-fill the feedback form with */ private String initialUserName; /** * Initial user's e-mail to pre-fill the feedback form with */ private String initialUserEmail; /** * Reference to this **/ private Context mContext; /** * Widgets and layout **/ private TextView mLastUpdatedTextView; private EditText mNameInput; private EditText mEmailInput; private EditText mSubjectInput; private EditText mTextInput; private Button mSendFeedbackButton; private Button mAddAttachmentButton; private Button mAddResponseButton; private Button mRefreshButton; private ScrollView mFeedbackScrollview; private LinearLayout mWrapperLayoutFeedbackAndMessages; private ListView mMessagesListView; /** * Send feedback {@link AsyncTask} */ private SendFeedbackTask mSendFeedbackTask; private Handler mFeedbackHandler; /** * Parse feedback {@link AsyncTask} */ private ParseFeedbackTask mParseFeedbackTask; private Handler mParseFeedbackHandler; /** * Initial attachment uris */ private List<Uri> mInitialAttachments; /** * URL for HockeyApp API **/ private String mUrl; /** * Current error for alert dialog **/ private ErrorObject mError; /** * Message data source **/ private MessagesAdapter mMessagesAdapter; private ArrayList<FeedbackMessage> mFeedbackMessages; /** * True when a message is posted **/ private boolean mInSendFeedback; /** * Indicates if a new thread should be created for each new feedback message as opposed to * the default resume thread behaviour. */ private boolean mForceNewThread; /** * True when the view was initialized **/ private boolean mFeedbackViewInitialized; /** * Unique token of the message feed **/ private String mToken; /** * Called when the activity is starting. Sets the title and content view * * @param savedInstanceState Data it most recently supplied in * onSaveInstanceState(Bundle) */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutView()); setTitle(getString(R.string.hockeyapp_feedback_title)); mContext = this; Bundle extras = getIntent().getExtras(); if (extras != null) { mUrl = extras.getString(EXTRA_URL); mForceNewThread = extras.getBoolean(EXTRA_FORCE_NEW_THREAD); initialUserName = extras.getString(EXTRA_INITIAL_USER_NAME); initialUserEmail = extras.getString(EXTRA_INITIAL_USER_EMAIL); Parcelable[] initialAttachmentsArray = extras.getParcelableArray(EXTRA_INITIAL_ATTACHMENTS); if (initialAttachmentsArray != null) { mInitialAttachments = new ArrayList<Uri>(); for (Parcelable parcelable : initialAttachmentsArray) { mInitialAttachments.add((Uri) parcelable); } } } if (savedInstanceState != null) { mFeedbackViewInitialized = savedInstanceState.getBoolean("feedbackViewInitialized"); mInSendFeedback = savedInstanceState.getBoolean("inSendFeedback"); } else { mInSendFeedback = false; mFeedbackViewInitialized = false; } // Cancel notification NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(ParseFeedbackTask.NEW_ANSWER_NOTIFICATION_ID); initFeedbackHandler(); initParseFeedbackHandler(); configureAppropriateView(); } /** * Restore all attachments. */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { if (savedInstanceState != null) { ViewGroup attachmentList = (ViewGroup) findViewById(R.id.wrapper_attachments); ArrayList<Uri> attachmentsUris = savedInstanceState.getParcelableArrayList("attachments"); for (Uri attachmentUri : attachmentsUris) { if (!mInitialAttachments.contains(attachmentUri)) { attachmentList.addView(new AttachmentView(this, attachmentList, attachmentUri, true)); } } mFeedbackViewInitialized = savedInstanceState.getBoolean("feedbackViewInitialized"); } super.onRestoreInstanceState(savedInstanceState); } /** * Save all attachments. */ @Override protected void onSaveInstanceState(Bundle outState) { AttachmentListView attachmentListView = (AttachmentListView) findViewById(R.id.wrapper_attachments); outState.putParcelableArrayList("attachments", attachmentListView.getAttachments()); outState.putBoolean("feedbackViewInitialized", mFeedbackViewInitialized); outState.putBoolean("inSendFeedback", mInSendFeedback); super.onSaveInstanceState(outState); } @Override protected void onStop() { super.onStop(); if (mSendFeedbackTask != null) { mSendFeedbackTask.detach(); } } /** * Detaches the activity from the send feedback task and returns the task * as last instance. This way the task is restored when the activity * is immediately re-created. * * @return The download task if present. */ @Override public Object onRetainNonConfigurationInstance() { if (mSendFeedbackTask != null) { mSendFeedbackTask.detach(); } return mSendFeedbackTask; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (mInSendFeedback) { mInSendFeedback = false; configureAppropriateView(); } else { finish(); } return true; } return super.onKeyDown(keyCode, event); } /** * Called when the Send Feedback {@link Button} is tapped. Sends the feedback and disables * the button to avoid multiple taps. */ @Override public void onClick(View v) { int viewId = v.getId(); if (viewId == R.id.button_send) { sendFeedback(); } else if (viewId == R.id.button_attachment) { ViewGroup attachments = (ViewGroup) findViewById(R.id.wrapper_attachments); if (attachments.getChildCount() >= MAX_ATTACHMENTS_PER_MSG) { //TODO should we add some more text here? Toast.makeText(this, String.valueOf(MAX_ATTACHMENTS_PER_MSG), Toast.LENGTH_SHORT).show(); } else { openContextMenu(v); } } else if (viewId == R.id.button_add_response) { mInSendFeedback = true; configureFeedbackView(false); } else if (viewId == R.id.button_refresh) { sendFetchFeedback(mUrl, null, null, null, null, null, PrefsUtil.getInstance().getFeedbackTokenFromPrefs(mContext), mFeedbackHandler, true); } } /** * Called when context menu is needed (on add attachment button). */ @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, ATTACH_FILE, 0, getString(R.string.hockeyapp_feedback_attach_file)); menu.add(0, ATTACH_PICTURE, 0, getString(R.string.hockeyapp_feedback_attach_picture)); } /** * Called when user clicked on context menu item. */ @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case ATTACH_FILE: case ATTACH_PICTURE: return addAttachment(item.getItemId()); default: return super.onContextItemSelected(item); } } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_ERROR_ID: return new AlertDialog.Builder(this) .setMessage(getString(R.string.hockeyapp_dialog_error_message)) .setCancelable(false) .setTitle(getString(R.string.hockeyapp_dialog_error_title)) .setIcon(android.R.drawable.ic_dialog_alert) .setPositiveButton(getString(R.string.hockeyapp_dialog_positive_button), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { mError = null; dialog.cancel(); } }).create(); } return null; } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch (id) { case DIALOG_ERROR_ID: AlertDialog messageDialogError = (AlertDialog) dialog; if (mError != null) { /** If the ErrorObject is not null, display the ErrorObject message */ messageDialogError.setMessage(mError.getMessage()); } else { /** If the ErrorObject is null, display the general error message */ messageDialogError.setMessage(getString(R.string.hockeyapp_feedback_generic_error)); } break; default: break; } } /** * Called when picture or file was chosen. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != RESULT_OK) { return; } if (requestCode == ATTACH_FILE) { /** User picked file */ Uri uri = data.getData(); if (uri != null) { final ViewGroup attachments = (ViewGroup) findViewById(R.id.wrapper_attachments); attachments.addView(new AttachmentView(this, attachments, uri, true)); } } else if (requestCode == ATTACH_PICTURE) { /** User picked image */ Uri uri = data.getData(); /** Start PaintActivity */ if (uri != null) { try { Intent intent = new Intent(this, PaintActivity.class); intent.putExtra(PaintActivity.EXTRA_IMAGE_URI, uri); startActivityForResult(intent, PAINT_IMAGE); } catch (ActivityNotFoundException e) { HockeyLog.error(Util.LOG_IDENTIFIER, "Paint activity not declared!", e); } } } else if (requestCode == PAINT_IMAGE) { /** Final attachment picture received and ready to be added to list. */ Uri uri = data.getParcelableExtra(PaintActivity.EXTRA_IMAGE_URI); if (uri != null) { final ViewGroup attachments = (ViewGroup) findViewById(R.id.wrapper_attachments); attachments.addView(new AttachmentView(this, attachments, uri, true)); } } else return; } @SuppressLint("InflateParams") public View getLayoutView() { return getLayoutInflater().inflate(R.layout.hockeyapp_activity_feedback, null); } /** * Enables/Disables the Send Feedback button. * * @param isEnable the button is enabled if true */ public void enableDisableSendFeedbackButton(boolean isEnable) { if (mSendFeedbackButton != null) { mSendFeedbackButton.setEnabled(isEnable); } } /** * Configures the content view by initializing the input {@link EditText}s * and the listener for the Send Feedback {@link Button} * * @param haveToken the message list is shown if true */ protected void configureFeedbackView(boolean haveToken) { mFeedbackScrollview = (ScrollView) findViewById(R.id.wrapper_feedback_scroll); mWrapperLayoutFeedbackAndMessages = (LinearLayout) findViewById(R.id.wrapper_messages); mMessagesListView = (ListView) findViewById(R.id.list_feedback_messages); if (haveToken) { /** If a token exists, the list of messages should be displayed */ mWrapperLayoutFeedbackAndMessages.setVisibility(View.VISIBLE); mFeedbackScrollview.setVisibility(View.GONE); mLastUpdatedTextView = (TextView) findViewById(R.id.label_last_updated); mLastUpdatedTextView.setVisibility(View.INVISIBLE); mAddResponseButton = (Button) findViewById(R.id.button_add_response); mAddResponseButton.setOnClickListener(this); mRefreshButton = (Button) findViewById(R.id.button_refresh); mRefreshButton.setOnClickListener(this); } else { /** if the token doesn't exist, the feedback details inputs to be sent need to be displayed */ mWrapperLayoutFeedbackAndMessages.setVisibility(View.GONE); mFeedbackScrollview.setVisibility(View.VISIBLE); mNameInput = (EditText) findViewById(R.id.input_name); mEmailInput = (EditText) findViewById(R.id.input_email); mSubjectInput = (EditText) findViewById(R.id.input_subject); mTextInput = (EditText) findViewById(R.id.input_message); configureHints(); /** Check to see if the Name and Email are saved in {@link SharedPreferences} */ if (!mFeedbackViewInitialized) { String nameEmailSubject = PrefsUtil.getInstance().getNameEmailFromPrefs(mContext); if (nameEmailSubject != null) { /** We have Name and Email. Prepopulate the appropriate fields */ String[] nameEmailSubjectArray = nameEmailSubject.split("\\|"); if (nameEmailSubjectArray != null && nameEmailSubjectArray.length >= 2) { mNameInput.setText(nameEmailSubjectArray[0]); mEmailInput.setText(nameEmailSubjectArray[1]); if (!mForceNewThread && nameEmailSubjectArray.length >= 3) { mSubjectInput.setText(nameEmailSubjectArray[2]); mTextInput.requestFocus(); } else { mSubjectInput.requestFocus(); } } } else { /** We dont have Name and Email. Check if initial values were provided */ mNameInput.setText(initialUserName); mEmailInput.setText(initialUserEmail); mSubjectInput.setText(""); if (TextUtils.isEmpty(initialUserName)) { mNameInput.requestFocus(); } else if (TextUtils.isEmpty(initialUserEmail)) { mEmailInput.requestFocus(); } else { mSubjectInput.requestFocus(); } } mFeedbackViewInitialized = true; } mNameInput.setVisibility(FeedbackManager.getRequireUserName() == FeedbackUserDataElement.DONT_SHOW ? View.GONE : View.VISIBLE); mEmailInput.setVisibility(FeedbackManager.getRequireUserEmail() == FeedbackUserDataElement.DONT_SHOW ? View.GONE : View.VISIBLE); /** Reset the remaining fields if previously populated */ mTextInput.setText(""); /** Check to see if the Feedback Token is available */ if ((!mForceNewThread || mInSendFeedback) && PrefsUtil.getInstance().getFeedbackTokenFromPrefs(mContext) != null) { /** If Feedback Token is available, hide the Subject Input field */ mSubjectInput.setVisibility(View.GONE); } else { /** If Feedback Token is not available, display the Subject Input field */ mSubjectInput.setVisibility(View.VISIBLE); } /** Reset the attachment list */ ViewGroup attachmentListView = (ViewGroup) findViewById(R.id.wrapper_attachments); attachmentListView.removeAllViews(); if (mInitialAttachments != null) { for (Uri attachmentUri : mInitialAttachments) { attachmentListView.addView(new AttachmentView(this, attachmentListView, attachmentUri, true)); } } /** Use of context menu needs to be enabled explicitly */ mAddAttachmentButton = (Button) findViewById(R.id.button_attachment); mAddAttachmentButton.setOnClickListener(this); registerForContextMenu(mAddAttachmentButton); mSendFeedbackButton = (Button) findViewById(R.id.button_send); mSendFeedbackButton.setOnClickListener(this); } } /** * Called when the request for sending the feedback has finished. * * @param success is true if the sending of the feedback was successful */ protected void onSendFeedbackResult(final boolean success) { } /** * Adds either file or picture attachment by intent picker. * * @param request Either ATTACH_FILE or ATTACH_PICTURE. */ private boolean addAttachment(int request) { if (request == ATTACH_FILE) { Intent intent = new Intent(); intent.setType("*/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getString(R.string.hockeyapp_feedback_select_file)), ATTACH_FILE); return true; } else if (request == ATTACH_PICTURE) { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getString(R.string.hockeyapp_feedback_select_picture)), ATTACH_PICTURE); return true; } else return false; } private void configureHints() { if (FeedbackManager.getRequireUserName() == FeedbackUserDataElement.REQUIRED) { mNameInput.setHint(getString(R.string.hockeyapp_feedback_name_hint_required)); } if (FeedbackManager.getRequireUserEmail() == FeedbackUserDataElement.REQUIRED) { mEmailInput.setHint(getString(R.string.hockeyapp_feedback_email_hint_required)); } mSubjectInput.setHint(getString(R.string.hockeyapp_feedback_subject_hint_required)); mTextInput.setHint(getString(R.string.hockeyapp_feedback_message_hint_required)); } private void configureAppropriateView() { /** Try to retrieve the Feedback Token from {@link SharedPreferences} */ if (!mForceNewThread || mInSendFeedback) { mToken = PrefsUtil.getInstance().getFeedbackTokenFromPrefs(this); } if (mToken == null || mInSendFeedback) { /** If Feedback Token is NULL, show the usual feedback view */ configureFeedbackView(false); } else { /** If Feedback Token is NOT NULL, show the Add Response Button and fetch the feedback messages */ configureFeedbackView(true); sendFetchFeedback(mUrl, null, null, null, null, null, mToken, mFeedbackHandler, true); } } /** * Initializes the {@link ParseFeedbackTask} * * @param feedbackResponseString JSON string response */ private void createParseFeedbackTask(String feedbackResponseString, String requestType) { mParseFeedbackTask = new ParseFeedbackTask(this, feedbackResponseString, mParseFeedbackHandler, requestType); } private void hideKeyboard() { if (mTextInput != null) { InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); manager.hideSoftInputFromWindow(mTextInput.getWindowToken(), 0); } } /** * Initializes the Feedback response {@link Handler} */ private void initFeedbackHandler() { mFeedbackHandler = new FeedbackHandler(this); } /** * Initialize the Feedback response parse result {@link Handler} */ private void initParseFeedbackHandler() { mParseFeedbackHandler = new ParseFeedbackHandler(this); } /** * Load the feedback messages fetched from server * * @param feedbackResponse {@link FeedbackResponse} object */ @SuppressLint("SimpleDateFormat") private void loadFeedbackMessages(final FeedbackResponse feedbackResponse) { runOnUiThread(new Runnable() { @Override public void run() { configureFeedbackView(true); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); SimpleDateFormat formatNew = new SimpleDateFormat("d MMM h:mm a"); Date date = null; if (feedbackResponse != null && feedbackResponse.getFeedback() != null && feedbackResponse.getFeedback().getMessages() != null && feedbackResponse. getFeedback().getMessages().size() > 0) { mFeedbackMessages = feedbackResponse.getFeedback().getMessages(); /** Reverse the order of the feedback messages list, so we show the latest one first */ Collections.reverse(mFeedbackMessages); /** Set the lastUpdatedTextView text as the date of the latest feedback message */ try { date = format.parse(mFeedbackMessages.get(0).getCreatedAt()); mLastUpdatedTextView.setText(String.format(getString(R.string.hockeyapp_feedback_last_updated_text), formatNew.format(date))); mLastUpdatedTextView.setContentDescription(mLastUpdatedTextView.getText()); mLastUpdatedTextView.setVisibility(View.VISIBLE); } catch (ParseException e1) { e1.printStackTrace(); } if (mMessagesAdapter == null) { mMessagesAdapter = new MessagesAdapter(mContext, mFeedbackMessages); } else { mMessagesAdapter.clear(); for (FeedbackMessage message : mFeedbackMessages) { mMessagesAdapter.add(message); } mMessagesAdapter.notifyDataSetChanged(); } mMessagesListView.setAdapter(mMessagesAdapter); } } }); } private void resetFeedbackView() { runOnUiThread(new Runnable() { @Override public void run() { PrefsUtil.getInstance().saveFeedbackTokenToPrefs(FeedbackActivity.this, null); getSharedPreferences(ParseFeedbackTask.PREFERENCES_NAME, 0) .edit() .remove(ParseFeedbackTask.ID_LAST_MESSAGE_SEND) .remove(ParseFeedbackTask.ID_LAST_MESSAGE_PROCESSED) .apply(); configureFeedbackView(false); } }); } /** * Send feedback to HockeyApp. */ private void sendFeedback() { if (!Util.isConnectedToNetwork(this)) { Toast errorToast = Toast.makeText(this, R.string.hockeyapp_error_no_network_message, Toast.LENGTH_LONG); errorToast.show(); return; } enableDisableSendFeedbackButton(false); hideKeyboard(); String token = mForceNewThread && !mInSendFeedback ? null : PrefsUtil.getInstance().getFeedbackTokenFromPrefs(mContext); String name = mNameInput.getText().toString().trim(); String email = mEmailInput.getText().toString().trim(); String subject = mSubjectInput.getText().toString().trim(); String text = mTextInput.getText().toString().trim(); if (TextUtils.isEmpty(subject)) { mSubjectInput.setVisibility(View.VISIBLE); setError(mSubjectInput, R.string.hockeyapp_feedback_validate_subject_error); } else if (FeedbackManager.getRequireUserName() == FeedbackUserDataElement.REQUIRED && TextUtils.isEmpty(name)) { setError(mNameInput, R.string.hockeyapp_feedback_validate_name_error); } else if (FeedbackManager.getRequireUserEmail() == FeedbackUserDataElement.REQUIRED && TextUtils.isEmpty(email)) { setError(mEmailInput, R.string.hockeyapp_feedback_validate_email_empty); } else if (TextUtils.isEmpty(text)) { setError(mTextInput, R.string.hockeyapp_feedback_validate_text_error); } else if (FeedbackManager.getRequireUserEmail() == FeedbackUserDataElement.REQUIRED && !Util.isValidEmail(email)) { setError(mEmailInput, R.string.hockeyapp_feedback_validate_email_error); } else { /** Save Name and Email to {@link SharedPreferences} */ PrefsUtil.getInstance().saveNameEmailSubjectToPrefs(mContext, name, email, subject); /** Make list for attachments file paths */ AttachmentListView attachmentListView = (AttachmentListView) findViewById(R.id.wrapper_attachments); List<Uri> attachmentUris = attachmentListView.getAttachments(); /** Start the Send Feedback {@link AsyncTask} */ sendFetchFeedback(mUrl, name, email, subject, text, attachmentUris, token, mFeedbackHandler, false); } } private void setError(EditText inputField, int feedbackStringId) { inputField.setError(getString(feedbackStringId)); enableDisableSendFeedbackButton(true); } /** * Initialize the {@link SendFeedbackTask} * * @param url URL to HockeyApp API * @param name Name of the feedback sender * @param email Email of the feedback sender * @param subject Message subject * @param text The message * @param token Token for message feed * @param feedbackHandler Handler to handle the response * @param isFetchMessages Set true to fetch messages, false to send one */ private void sendFetchFeedback(String url, String name, String email, String subject, String text, List<Uri> attachmentUris, String token, Handler feedbackHandler, boolean isFetchMessages) { mSendFeedbackTask = new SendFeedbackTask(mContext, url, name, email, subject, text, attachmentUris, token, feedbackHandler, isFetchMessages); AsyncTaskUtils.execute(mSendFeedbackTask); } /** * Creates and starts execution of the {@link ParseFeedbackTask} * * @param feedbackResponseString JSON string response */ private void startParseFeedbackTask(String feedbackResponseString, String requestType) { createParseFeedbackTask(feedbackResponseString, requestType); AsyncTaskUtils.execute(mParseFeedbackTask); } private static class FeedbackHandler extends Handler { private final WeakReference<FeedbackActivity> mWeakFeedbackActivity; public FeedbackHandler(FeedbackActivity feedbackActivity) { mWeakFeedbackActivity = new WeakReference<>(feedbackActivity); } @Override public void handleMessage(Message msg) { boolean success = false; ErrorObject error = new ErrorObject(); final FeedbackActivity feedbackActivity = mWeakFeedbackActivity.get(); if (feedbackActivity == null) { return; } if (msg != null && msg.getData() != null) { Bundle bundle = msg.getData(); String responseString = bundle.getString(SendFeedbackTask.BUNDLE_FEEDBACK_RESPONSE); String statusCode = bundle.getString(SendFeedbackTask.BUNDLE_FEEDBACK_STATUS); String requestType = bundle.getString(SendFeedbackTask.BUNDLE_REQUEST_TYPE); if ("send".equals(requestType) && (responseString == null || Integer.parseInt(statusCode) != 201)) { // Send feedback went wrong if response is empty or status code != 201 error.setMessage(feedbackActivity.getString(R.string.hockeyapp_feedback_send_generic_error)); } else if ("fetch".equals(requestType) && statusCode != null && (Integer.parseInt(statusCode) == 404 || Integer.parseInt(statusCode) == 422)) { // Fetch feedback went wrong if status code is 404 or 422 feedbackActivity.resetFeedbackView(); success = true; } else if (responseString != null) { feedbackActivity.startParseFeedbackTask(responseString, requestType); success = true; } else { error.setMessage(feedbackActivity.getString(R.string.hockeyapp_feedback_send_network_error)); } } else { error.setMessage(feedbackActivity.getString(R.string.hockeyapp_feedback_send_generic_error)); } feedbackActivity.mError = error; if (!success) { feedbackActivity.runOnUiThread(new Runnable() { @SuppressWarnings("deprecation") @Override public void run() { feedbackActivity.enableDisableSendFeedbackButton(true); feedbackActivity.showDialog(DIALOG_ERROR_ID); } }); } feedbackActivity.onSendFeedbackResult(success); } } private static class ParseFeedbackHandler extends Handler { private final WeakReference<FeedbackActivity> mWeakFeedbackActivity; public ParseFeedbackHandler(FeedbackActivity feedbackActivity) { mWeakFeedbackActivity = new WeakReference<>(feedbackActivity); } @Override public void handleMessage(Message msg) { boolean success = false; final FeedbackActivity feedbackActivity = mWeakFeedbackActivity.get(); if (feedbackActivity == null) { return; } feedbackActivity.mError = new ErrorObject(); if (msg != null && msg.getData() != null) { Bundle bundle = msg.getData(); FeedbackResponse feedbackResponse = (FeedbackResponse) bundle.getSerializable(ParseFeedbackTask.BUNDLE_PARSE_FEEDBACK_RESPONSE); if (feedbackResponse != null) { if (feedbackResponse.getStatus().equalsIgnoreCase("success")) { /** We have a valid result from JSON parsing */ success = true; if (feedbackResponse.getToken() != null) { /** Save the Token to SharedPreferences */ PrefsUtil.getInstance().saveFeedbackTokenToPrefs(feedbackActivity, feedbackResponse.getToken()); /** Load the existing feedback messages */ feedbackActivity.loadFeedbackMessages(feedbackResponse); feedbackActivity.mInSendFeedback = false; } } else { success = false; } } } /** Something went wrong, so display an error dialog */ if (!success) { feedbackActivity.runOnUiThread(new Runnable() { @SuppressWarnings("deprecation") @Override public void run() { feedbackActivity.showDialog(DIALOG_ERROR_ID); } }); } feedbackActivity.enableDisableSendFeedbackButton(true); } } }