Java tutorial
package com.intuit.qboecoui.email; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.ServiceConnection; import android.content.res.Configuration; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.support.v4.content.FileProvider; import android.text.TextUtils; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import com.android.volley.Response.ErrorListener; import com.android.volley.Response.Listener; import com.android.volley.VolleyError; import com.intuit.elves.app.BaseApplicationModule; import com.intuit.elves.log.CustomLog; import com.intuit.elves.network.CustomError; import com.intuit.elves.network.NetworkUtil; import com.intuit.qboecocomp.datasync.ISyncConstants; import com.intuit.qboecocomp.datasync.entity.QBODocumentLinkEntity; import com.intuit.qboecocomp.datasync.entity.SalesFormsEmailEntity; import com.intuit.qboecocomp.datasync.entity.SalesFormsEmailEntity.TXN_TYPE; import com.intuit.qboecocomp.json.request.V3Operation; import com.intuit.qboecocomp.network.AddEmailRequest; import com.intuit.qboecocomp.network.GetCompanyPrefDataRequest; import com.intuit.qboecocomp.network.UpdateSFMPreferenceAndSendEmailRequest; import com.intuit.qboecocomp.qbo.attachable.model.AttachableDataAccessor; import com.intuit.qboecocomp.qbo.common.data.AttachableAssociation; import com.intuit.qboecocomp.qbo.common.model.DataHelper; import com.intuit.qboecocomp.qbo.companyinfo.model.CompanyInfoDetails; import com.intuit.qboecocomp.qbo.companyinfo.model.QBCompanyInfoDataAccessor; import com.intuit.qboecocomp.qbo.estimate.model.EstimateManager; import com.intuit.qboecocomp.qbo.invoice.model.InvoiceManager; import com.intuit.qboecocomp.qbo.prefs.data.Preference; import com.intuit.qboecocomp.qbo.salesreceipt.model.SalesReceiptManager; import com.intuit.qboecocomp.qbo.transaction.model.ContactData; import com.intuit.qboecocomp.qbo.transaction.model.TransactionData; import com.intuit.qboecocomp.qbo.transaction.model.TransactionManager; import com.intuit.qboecocomp.tracking.TrackConstants; import com.intuit.qboecocomp.util.FormattingUtility; import com.intuit.qboecocomp.util.Util; import com.intuit.qboecocore.app.QBOApplicationModule; import com.intuit.qboecocore.auth.response.Response; import com.intuit.qboecocore.common.BaseAppPreferences; import com.intuit.qboecocore.exception.PSQBOServiceErrorCodes; import com.intuit.qboecocore.exception.QBException; import com.intuit.qboecocore.json.V3SalesFormEmailJson; import com.intuit.qboecocore.network.StringResponse; import com.intuit.qboecocore.network.exception.IErrorCodes; import com.intuit.qboecocore.network.response.IResponseCode; import com.intuit.qboecoui.R; import com.intuit.qboecoui.common.AppPreferences; import com.intuit.qboecoui.common.aidl.IActivityCallback; import com.intuit.qboecoui.common.aidl.IServiceCallback; import com.intuit.qboecoui.common.ui.BaseFragment; import com.intuit.qboecoui.common.ui.BaseSinglePaneActivity; import com.intuit.qboecoui.common.ui.dialog.ErrorDialog; import com.intuit.qboecoui.paymentshub.utils.CreditCardTypeUtils; import com.intuit.qboecoui.qbo.preview.PreviewResponseHandler; import com.intuit.qboecoui.qbo.preview.PreviewService; import com.intuit.qboecoui.tracking.QBMTrackConstants; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Date; import pdftron.Common.PDFNetException; import pdftron.PDF.PDFDoc; import pdftron.PDF.PDFNet; import pdftron.PDF.PDFViewCtrl; // TODO : Re-factor this class for better maintainability. (Kbhalla sept 5 2014) /** * This activity handles the sales form email for the following sales * transactions: 1. Estimates 2. Invoices 3. Sales Receipts * * The generic email functionality includes: * 1. Open the activity with controls configured for emailing transaction * 1.1. Load the message defaults for subject and body from company preferences * 1.2. Load the email address(es) received as part of the transaction payload * 1.3. Customize the form UI based on the transaction being emailed and the company preferences * * 2. Sending email * 2.1 Validating form fields * 2.1.1 Email addresses (single and multiple) * 2.1.2 subject and body content * 2.2 Checks company preferences for the * * 3. Preview transaction before sending the email on the form * 3.1 Use PDF Tron to render the pdf in same form. (TODO P1 - Change this to android PDF renderer available from minSDK 21 lollipop) * 3.1.1 Support the zoom and scroll for the rendered pdf in control * 3.1.2 Launch the external PDF viewer when the pdf is clicked. * * * * @author kbhalla * */ public class SalesFormEmail extends BaseSinglePaneActivity implements OnClickListener, Listener<StringResponse>, ErrorListener { private final static String TAG = "SalesFormEmail"; // Projection for reading the preference table and get the required preferences private static final String sfm_preference_projections[] = new String[] { Preference.ETRANSACTION_ENABLED_STATUS, Preference.ETRANSACTION_PAYMENT_ENABLED, Preference.ETRANSACTION_ATTACH_PDF, Preference.INVOICE_MESSAGE, Preference.INVOICE_SUBJECT, Preference.SALESRECEIPT_MESSAGE, Preference.SALESRECEIPT_SUBJECT, Preference.ESTIMATE_MESSAGE, Preference.ESTIMATE_SUBJECT }; private final static String SUPPORTED_EMAIL_SEPARATOR = ","; private final static int SUPPORTED_EMAIL_BODY_CONTENT_SIZE = 4000; private final static double US_LETTER_HEIGHT_WIDTH_RATIO = 1.29; private PDFViewCtrl mPDFViewCtrl; // UI control to load and render the pdf into. private LinearLayout mPdfLoadingMessage; // control to show the pdf loading message // mCurrentCommandRequest is used to track which service request is currently being processed. private int mCurrentCommandRequest = 0; // hasPreviewLaunched is used to ensure that only one instance of external pdf viewer is launched. private static boolean hasPreviewLaunched = false; @Override public void onBackPressed() { // Unbind the preview service when the back button is pressed. if (mPreviewHandler != null) { ((PreviewResponseHandler) mPreviewHandler).onFinish(); } super.onBackPressed(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final int titleResId = R.string.send_email_title; setTitle(titleResId); // Extract URI of the transaction that initiated the Sales form email. mUri = getIntent().getData(); // set the transaction type and transaction manager objects. setTransactionType(getIntent().getExtras().getInt(SalesFormsEmailEntity.TXN_TYPE_KEY)); // Create a object of the JSON entity. this will be used to hold the // form data and create the payload for the "Send" endpoint. mSalesFormEmailJson = new V3SalesFormEmailJson(); // Initialize the library with the license key. try { PDFNet.initialize(this, R.raw.pdfnet, "Intuit Inc.(intuit.com):OEM:QuickBooks Online[QBO]::LMIA:AMS(20150103):3E7723C11F273AD0330FAD7860611FA5C7581A63CB22056B1CBF0EF024DABEF5C7"); } catch (PDFNetException e) { CustomLog.logDebug(TAG, "onCreate - initialize PDFNet"); } // Inflate the view and get a reference to PDFViewCtrl setContentView(R.layout.v3email_layout); // setting up action bar getActivityHelper().setupActionBar(mTitle, false, false, true); getActivityHelper().showActionBar(); // load the email preferences from data base. loadEmailMessagePrefsFromDB(this.getApplicationContext()); // Customize the send email form based on the transaction type and // company file preferences customizeSalesFormEmailForm(); BaseApplicationModule.getTrackingModule().trackPage(QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME); //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME); // Load the default values for the sales form email form, from // preferences and transaction entity. loadDefaultSalesFormEmailFormData(); // setting up action bar getActivityHelper().setupActionBar(mTitle, false, false, true); getActivityHelper().showActionBar(); CustomLog.logDebug(TAG, "SalesFormEmail: onCreate"); mPDFViewCtrl = (PDFViewCtrl) findViewById(R.id.pdfviewctrl); mPdfLoadingMessage = (LinearLayout) findViewById(R.id.pdf_loading); mPDFViewCtrl.setVisibility(View.GONE); mPdfLoadingMessage.setVisibility(View.VISIBLE); // SalesFormEmail.SFM_Flow_tracking = ""; preview(mUri); } /* * This adds items to the menu section that is showing at the bottom of the * screen. * * @see * com.intuit.qboecoui.quickbooks.ui.QuickBooksActivity#onCreateOptionsMenu(android * .view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { final MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.sales_form_email_details_menu, menu); return super.onCreateOptionsMenu(menu); } @Override protected BaseFragment onCreatePane() { // TODO Auto-generated method stub return null; } /** * This method displays the dialog asking the user to turn on the attach pdf * preference and send the email. */ public void attachPDFPreferencePopup() { new AlertDialog.Builder(this).setMessage(R.string.sfm_turn_on_attach_pdf_preference) .setTitle(R.string.sfm_turn_on_attach_pdf_preference_title) .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); BaseApplicationModule.getTrackingModule().trackLink( QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME, QBMTrackConstants.TRANSACTION_SAVE_YES_ACTION); handleUpdatePreference(); } }).setNeutralButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); // User does not want to set the preference. We will not show the OTM again. AppPreferences.setBooleanPreference( QBOApplicationModule.getInstance().getApplicationContext(), null, AppPreferences.KEY_ATTACH_PDF_PREFERENCE_OTM_SHOW, false); BaseApplicationModule.getTrackingModule().trackLink( QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME, QBMTrackConstants.TRANSACTION_SAVE_NO_ACTION); issueSendEmailRequest(); } }).show(); } /** * This method prepares the command to update the company preference and send the email. * @return */ private boolean handleUpdatePreference() { // If there is no error in form data send the request. JSONObject jsonObj = new JSONObject(); try { jsonObj.put(V3Operation.SEND, mSalesFormEmailJson.toSerialize()); } catch (JSONException e1) { CustomLog.logError(TAG, e1, "SalesFormEmail: problem deteceted while serializing the send payload."); return false; } String jsonPayload = ""; try { jsonPayload = (jsonObj.get(V3Operation.SEND)).toString(); } catch (JSONException exception) { CustomLog.logError(TAG, exception, "SalesFormEmail: Error retrieving the payload"); return false; } // handle update of the attach pdf preference. final ContentValues pContentValues = new ContentValues(2); pContentValues.clear(); pContentValues.put(Preference._ID, 1); pContentValues.put(Preference.ETRANSACTION_ATTACH_PDF, "true"); mCurrentCommandRequest = ISyncConstants.FLAG_EDIT_PREF_AND_SEND_MAIL; Uri prefUri = getApplicationContext().getContentResolver().insert(Preference.CONTENT_URI, pContentValues); UpdateSFMPreferenceAndSendEmailRequest updateRequest = UpdateSFMPreferenceAndSendEmailRequest.createRequest( BaseApplicationModule.getNetworkModule(), this, ISyncConstants.FLAG_EDIT_PREF_AND_SEND_MAIL, prefUri, mUri, jsonPayload, mTransactionType, this, this); updateRequest.setTag(this); // TODO P1 : check this code for case when the update preference fails / passes. BaseApplicationModule.getNetworkModule().dispatchRequest(updateRequest); try { // We shoud stop the pdf preview service here this.stopService(new Intent(this, PreviewService.class)); ((PreviewResponseHandler) mPreviewHandler).onFinish(); } catch (SecurityException ex) { // do nothing } // track the click of send button //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SALES_FORM_EMAIL_SENT ); BaseApplicationModule.getTrackingModule().trackLink(QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME, QBMTrackConstants.SALES_FORM_EMAIL_SENT_WITH_PREF_UPDATE); startProgressBar(getString(R.string.sales_form_email_sending_email_text)); return true; } /** * This method determines if the OTM seeking the user to turn on the PDF attach preference is to be handled. * * @return true when the pop-up is displayed / handled */ private boolean handleAttachPDF_OTM() { boolean handleAttachPDFOTM = false; // Show the pop-up /* * check 1. In preference if the pop-up has been shown before? if not * check 2. Is the company neo enabled? and eTransactionEnabledStatus is enabled and the attachPDFFlag is turned off. If true * Show the pop-up and flag it in the settings ... */ final boolean showOTM = AppPreferences.getBooleanPreference( QBOApplicationModule.getInstance().getApplicationContext(), null, AppPreferences.KEY_ATTACH_PDF_PREFERENCE_OTM_SHOW, true); if (showOTM && mTransactionType == TXN_TYPE.INVOICE && Util.isNeoEnabled() && SalesFormEmail.ETransactionEnabledStatus_ENABLED.equalsIgnoreCase(mETransactionEnabledStatus) && !mETransactionAttachPDF) { // Show the popup handleAttachPDFOTM = true; attachPDFPreferencePopup(); } return handleAttachPDFOTM; } /* * Handle Menu clicks for this screen. * * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) */ @Override public boolean onOptionsItemSelected(MenuItem item) { final int id = item.getItemId(); if (id == R.id.sales_form_email_send) { retrieveSalesFormEmailData(mSalesFormEmailJson); // Check if there is no internet connectivity. if (!NetworkUtil.checkInternetConnection(this)) { new ErrorDialog(this, getString(R.string.login_network_error), getString(R.string.error_title_unable_to_connect)); return false; } // Validate form data and handle that. String errorString = validateFromData(mSalesFormEmailJson); if (!TextUtils.isEmpty(errorString)) { errorString = getString(R.string.sales_form_email_error_header_message) + errorString; new ErrorDialog(this, errorString, getString(R.string.sales_form_email_error_title)); } else { if (!handleAttachPDF_OTM()) { return issueSendEmailRequest(); } } } else { return super.onOptionsItemSelected(item); } return true; } private boolean issueSendEmailRequest() { if (!prepareAndSendTheRequest()) { stopProgressBar(); return false; // in case of error in sending the request we // log the error and return false to the // caller. // TODO P1 - check where is this flow heading to. } else { // Form data is validated and request is sent. Start the // progress bar. startProgressBar(getString(R.string.sales_form_email_sending_email_text)); return true; } } public void onCallback(int op, int responseCode, String pMessage) { CustomLog.logDebug(TAG, "SalesFormEmail : onCallback : responseCode is " + responseCode); mHandler.sendMessage(mHandler.obtainMessage(op, responseCode, 0, pMessage)); } // ----------------------------- // Private members of the class // ----------------------------- private Uri mUri = null; // holds the URI of the transaction we want to send // email of. private V3SalesFormEmailJson mSalesFormEmailJson = null; // JSON data // container for // email data. private ProgressDialog mProgressDialog; private TXN_TYPE mTransactionType; // This enum holds the transaction type // for which the email needs to be sent. private TransactionManager mTransactionManager; // holds the reference of // the manager object of the // respective transaction. /** * ETransactionEnabledStatus Specifies if ETransaction (eInvoicing) is * enabled or not. Supported values are Enabled, NotApplicable */ private String mETransactionEnabledStatus = ""; /** * ETransactionAttachPDF Attach Sales PDF with eTransaction email When as * Input, processed only when "ETransactionEnabledStatusEnum" is Enabled. * Otherwise ignored. */ private boolean mETransactionAttachPDF = false; /** * ETransactionPaymentEnabled Will be true if MAS subscription is Approved * and region is US */ private boolean mETransactionPaymentEnabled = false; public static String ETransactionEnabledStatus_ENABLED = "Enabled"; /** * This method sets the mTransactionType member to current sales form * transaction which we need to send email for. */ private void setTransactionType(int aTransactionCode) { if (aTransactionCode == TXN_TYPE.ESTIMATE.getValue()) { mTransactionType = TXN_TYPE.ESTIMATE; mTransactionManager = new EstimateManager(); } else if (aTransactionCode == TXN_TYPE.SALES_RECEIPT.getValue()) { mTransactionType = TXN_TYPE.SALES_RECEIPT; mTransactionManager = new SalesReceiptManager(); } else { mTransactionType = TXN_TYPE.INVOICE; mTransactionManager = new InvoiceManager(); } } /** * This method customizes the sales form email form based on the transaction * type and company file preferences. This determines if preferences for * online payment will be displayed or not. */ private void customizeSalesFormEmailForm() { if (mTransactionType == TXN_TYPE.INVOICE) { // We will show the online payment options only if the company file // is a Harmony file. // EInvoicing is enabled and MAS subscription is paid. if (Util.isNeoEnabled()) { LinearLayout onlinePaymentPreferencesContainer = (LinearLayout) findViewById( R.id.pref_online_payment_container); if (onlinePaymentPreferencesContainer != null && SalesFormEmail.ETransactionEnabledStatus_ENABLED .equalsIgnoreCase(mETransactionEnabledStatus) && mETransactionPaymentEnabled) { // Show these preferences onlinePaymentPreferencesContainer.setVisibility(View.VISIBLE); } if (Util.shouldBankTransferOptionBeShown()) { onlinePaymentPreferencesContainer.findViewById(R.id.pref_bank_transfer) .setVisibility(View.VISIBLE); } else { onlinePaymentPreferencesContainer.findViewById(R.id.pref_bank_transfer) .setVisibility(View.GONE); } } } } private final Handler mHandler = new Handler() { // Process the message generated at the time of QBDataSyncService // processing @Override public void handleMessage(Message msg) { handleDataSyncResponse(msg); } }; /** * This method prepares and sends the sales form email request to the v3 end * point. */ private boolean prepareAndSendTheRequest() { // If there is no error in form data send the request. JSONObject jsonObj = new JSONObject(); try { jsonObj.put(V3Operation.SEND, mSalesFormEmailJson.toSerialize()); } catch (JSONException e1) { CustomLog.logError(TAG, e1, "SalesFormEmail: problem deteceted while serializing the send payload."); return false; } String jsonPayload = ""; try { jsonPayload = (jsonObj.get(V3Operation.SEND)).toString(); } catch (JSONException exception) { CustomLog.logError(TAG, exception, "SalesFormEmail: Error retrieving the payload"); return false; } mCurrentCommandRequest = ISyncConstants.FLAG_ADD_EMAIL; AddEmailRequest request = AddEmailRequest.createRequest(BaseApplicationModule.getNetworkModule(), this, ISyncConstants.FLAG_ADD_EMAIL, mUri, jsonPayload, mTransactionType, this, this); request.setTag(this); BaseApplicationModule.getNetworkModule().dispatchRequest(request); try { // We should stop the pdf preview service here as we are sending the request for sending email this.stopService(new Intent(this, PreviewService.class)); ((PreviewResponseHandler) mPreviewHandler).onFinish(); } catch (SecurityException ex) { // do nothing /* This function will throw {@link SecurityException} if you do not * have permission to stop the given service. */ } // track the click of send button //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SALES_FORM_EMAIL_SENT ); BaseApplicationModule.getTrackingModule().trackLink(QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME, QBMTrackConstants.SALES_FORM_EMAIL_SENT); return true; } /** * This method validates the Sales form email data. * * @param salesFormEmailJson * @return */ private String validateFromData(V3SalesFormEmailJson salesFormEmailJson) { String returnValue = ""; if (TextUtils.isEmpty(salesFormEmailJson.EmailMessage.Subject)) { returnValue += getString(R.string.sales_form_email_error_missing_subject); } if (salesFormEmailJson.EmailMessage.Message.length() > SUPPORTED_EMAIL_BODY_CONTENT_SIZE) { returnValue += getString(R.string.sales_form_email_error_huge_email_content); } if (TextUtils.isEmpty(salesFormEmailJson.DeliveryAddress.Address)) { returnValue += getString(R.string.sales_form_email_error_missing_email_id); } else { salesFormEmailJson.DeliveryAddress.Address = salesFormEmailJson.DeliveryAddress.Address.replace(";", SUPPORTED_EMAIL_SEPARATOR); // In case of multiple emails split them and validate each one. final String emailAddressesString = salesFormEmailJson.DeliveryAddress.Address; String[] emailAddressesTokenized = emailAddressesString.split(SUPPORTED_EMAIL_SEPARATOR); if (!Util.validateMultipleEmails(emailAddressesTokenized)) { returnValue += getString(R.string.sales_form_email_error_invalid_email_id); } else { String validatedEmailAddresses = ""; for (String anEmailAddress : emailAddressesTokenized) { if (anEmailAddress != null && !TextUtils.isEmpty(anEmailAddress.trim())) { validatedEmailAddresses += anEmailAddress.trim() + SUPPORTED_EMAIL_SEPARATOR; } } // remove the trailing "," from validated email addresses if (!TextUtils.isEmpty(validatedEmailAddresses)) { validatedEmailAddresses = validatedEmailAddresses.substring(0, validatedEmailAddresses.lastIndexOf(SUPPORTED_EMAIL_SEPARATOR)); } salesFormEmailJson.DeliveryAddress.Address = validatedEmailAddresses; } } return returnValue; } /** * This method fetches the data from the form and populates the * V3SalesFormEmailJson object. * * @param salesFormEmailJson * Return parameter that will be loaded with form data. */ private void retrieveSalesFormEmailData(V3SalesFormEmailJson salesFormEmailJson) { final EditText emailReceiver = (EditText) findViewById(R.id.email_receiver); if (null != emailReceiver) { salesFormEmailJson.DeliveryAddress.Address = emailReceiver.getText().toString(); } final EditText emailSubject = (EditText) findViewById(R.id.email_subject); if (null != emailSubject) { salesFormEmailJson.EmailMessage.Subject = emailSubject.getText().toString(); } final EditText emailBody = (EditText) findViewById(R.id.email_body); if (null != emailBody) { salesFormEmailJson.EmailMessage.Message = emailBody.getText().toString(); } // The online payment preferences picked up from the form to be sent in // the JSON request. final CompoundButton switchBankTransfer = (CompoundButton) findViewById(R.id.switch_bank_transfer); final CompoundButton switchCreditCard = (CompoundButton) findViewById(R.id.switch_credit_card); salesFormEmailJson.AllowOnlineACHPayment = Boolean.toString(switchBankTransfer.isChecked()); salesFormEmailJson.AllowOnlineCreditCardPayment = Boolean.toString(switchCreditCard.isChecked()); } /** * This method is used for loading any global preferences saved in the pref * table. */ private void loadEmailMessagePrefsFromDB(Context pContext) { Cursor cursor = null; try { cursor = pContext.getContentResolver().query(Preference.CONTENT_URI, sfm_preference_projections, null, null, null); if (cursor != null && cursor.moveToNext()) { mETransactionEnabledStatus = cursor.getString(0); mETransactionPaymentEnabled = (cursor.getString(1).equalsIgnoreCase("true") ? true : false); mETransactionAttachPDF = (cursor.getString(2).equalsIgnoreCase("true") ? true : false); final String invoiceMessage = cursor.getString(3); final String invoiceSubject = cursor.getString(4); final String salesReceiptMessage = cursor.getString(5); final String salesReceiptSubject = cursor.getString(6); final String estimateMessage = cursor.getString(7); final String estimateSubject = cursor.getString(8); if (mTransactionType == TXN_TYPE.INVOICE) { mSalesFormEmailJson.EmailMessage.Subject = invoiceSubject; mSalesFormEmailJson.EmailMessage.Message = invoiceMessage; } else if (mTransactionType == TXN_TYPE.ESTIMATE) { mSalesFormEmailJson.EmailMessage.Subject = estimateSubject; mSalesFormEmailJson.EmailMessage.Message = estimateMessage; } else if (mTransactionType == TXN_TYPE.SALES_RECEIPT) { mSalesFormEmailJson.EmailMessage.Subject = salesReceiptSubject; mSalesFormEmailJson.EmailMessage.Message = salesReceiptMessage; } } } catch (final Exception ex) { CustomLog.logError(TAG, ex, "Error reading email preferences from database"); // any error in reading/setting prefs should take user out of the // app throw new QBException(IErrorCodes.ERROR_READING_CURRENCY_FROM_DB); } finally { if (cursor != null) { cursor.close(); cursor = null; } } } /** * This method populates the form with following default data: 1. Sender's * address from the transaction object. 2. Subject and body of the email * from sales form preferences for the specific transaction. 3. Sync token * from the transaction. * */ private void loadDefaultSalesFormEmailFormData() { if (mUri != null) { mTransactionManager.retrieveTransactionDetails(mUri); } TransactionData txnData = mTransactionManager.getTxnData(); mSalesFormEmailJson.SyncToken = txnData.syncToken; // TODO P2 Check what use case is this? and document this. if (mTransactionManager instanceof SalesReceiptManager) { SalesReceiptManager srManager = (SalesReceiptManager) mTransactionManager; if (srManager.isProcessedPayment()) { StringBuilder sb = new StringBuilder(); sb.append(QBOApplicationModule.getInstance().getApplicationContext().getResources() .getString(R.string.sales_form_email_payment_summary)); String str = srManager.getTxnData().currency; double dbl = srManager.getTotal(); if (str != null) { str = FormattingUtility.formatCurrency(dbl, str); } else { str = FormattingUtility.formatCurrency(dbl); } sb.append(String.format(QBOApplicationModule.getInstance().getApplicationContext().getResources() .getString(R.string.sales_form_email_amount), str)); if (srManager.getDateCalendar() != null) { long txnDate = srManager.getDateCalendar().getTimeInMillis(); if (txnDate > 0) { str = FormattingUtility.formatDate(new Date(txnDate)); } sb.append(String.format("%s %s\n", getString(R.string.date_title), str)); } ContactData contact = srManager.getContact(); if (contact != null) { sb.append(String.format(QBOApplicationModule.getInstance().getApplicationContext() .getResources().getString(R.string.sales_form_email_paid_by), contact.name)); } CompanyInfoDetails companyInfo = QBCompanyInfoDataAccessor.retrieveCompanyInfo(); String companyName = companyInfo.legalname != null ? companyInfo.legalname : companyInfo.companyname; sb.append(String.format(QBOApplicationModule.getInstance().getApplicationContext().getResources() .getString(R.string.sales_form_email_paid_to), companyName)); if (companyInfo.legal_addr_city != null && companyInfo.legal_addr_subcode != null) { sb.append( String.format("%s, %s\n", companyInfo.legal_addr_city, companyInfo.legal_addr_subcode)); } str = CreditCardTypeUtils.parseLastFourDigits(srManager.getObfuscatedCardNumber()); sb.append(String.format(QBOApplicationModule.getInstance().getApplicationContext().getResources() .getString(R.string.sales_form_email_card), str)); sb.append(String.format("%s: %s", getString(R.string.processed_payment_auth), srManager.getPaymentAuthCode())); mSalesFormEmailJson.EmailMessage.Message = mSalesFormEmailJson.EmailMessage.Message .concat(sb.toString()); } } // Get the count of attachments int countOfAttachments = 0; try { countOfAttachments = AttachableDataAccessor.getCountOfAttachments(Long.toString(txnData.id), AttachableAssociation.INCLUDE_ON_SEND_URI); } catch (QBException QBe) { CustomLog.logDebug(TAG, "SalesFormEmail : " + QBe.getMessage()); } final TextView attachmentCount = (TextView) findViewById(R.id.attachment_count); if (attachmentCount != null) { if (countOfAttachments == 0) { final LinearLayout attachmentContainer = (LinearLayout) findViewById( R.id.attachment_count_container); if (attachmentContainer != null) { attachmentContainer.setVisibility(View.GONE); } } else if (countOfAttachments == 1) { attachmentCount.setText(getString(R.string.sales_form_email_attachment_text_1)); } else { attachmentCount .setText(String.format(getString(R.string.sales_form_email_attachment_text_morethan1), Integer.toString(countOfAttachments))); } } final EditText emailReceiver = (EditText) findViewById(R.id.email_receiver); if (emailReceiver != null) { if (!TextUtils.isEmpty(txnData.mBill_email)) { emailReceiver.setText(txnData.mBill_email); } else if (txnData != null && txnData.mContact != null && !TextUtils.isEmpty(txnData.mContact.externalId)) { // Whenever the customer on the sales transaction is changed we will be changing the email address associated with the sales transaction. final String[] customerEmail = DataHelper.retrieveEmailAddress( QBOApplicationModule.getInstance().getApplicationContext(), txnData.mContact.externalId); if (customerEmail != null && customerEmail.length > 0) { emailReceiver.setText(customerEmail[0]); } } } final EditText emailSubject = (EditText) findViewById(R.id.email_subject); if (emailSubject != null) { emailSubject.setText(mSalesFormEmailJson.EmailMessage.Subject); } final EditText emailBody = (EditText) findViewById(R.id.email_body); if (emailBody != null) { emailBody.setText(mSalesFormEmailJson.EmailMessage.Message); } // The preferences for online payment and bank payment picked up from // the transaction object and set on the form. final CompoundButton switchCreditCard = (CompoundButton) findViewById(R.id.switch_credit_card); mSalesFormEmailJson.AllowOnlineCreditCardPayment = Boolean .toString(txnData.mAllow_Online_Credit_Card_Payment); if (txnData.mAllow_Online_Credit_Card_Payment) { switchCreditCard.setChecked(true); } else { switchCreditCard.setChecked(false); } if (Util.shouldBankTransferOptionBeShown()) { final CompoundButton switchBankTransfer = (CompoundButton) findViewById(R.id.switch_bank_transfer); mSalesFormEmailJson.AllowOnlineACHPayment = Boolean.toString(txnData.getAllow_Online_Ach_Payment()); if (txnData.getAllow_Online_Ach_Payment()) { switchBankTransfer.setChecked(true); } else { switchBankTransfer.setChecked(false); } } } private ProgressDialog mProgress; /* * This method generates the PDF and downloads the PDF. */ private void preview(Uri pdfUri) { CustomLog.logDebug(TAG, "QBOViewTransactionFragment : preview() started"); final Intent intent = new Intent(this, PreviewService.class); intent.setData(pdfUri); this.bindService(intent, mPreviewServiceConnection, Context.BIND_AUTO_CREATE); this.startService(intent); // TODO P1 : Check if we need to show progress "mProgress" bar here to indicate rendering in pdf viewer. } /** * AIDL class to bind aynchronous request service with this UI element so * that it can receive updates */ private ServiceConnection mPreviewServiceConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { final IServiceCallback delegate = IServiceCallback.Stub.asInterface(service); try { delegate.register(mPreviewCallback); } catch (final RemoteException e) { CustomLog.logError(TAG, e, "SalesFormEmail : Error with IPC"); } } }; /** * AIDL callback method from the service running in the background */ private final IActivityCallback.Stub mPreviewCallback = new IActivityCallback.Stub() { @Override public void callback(int op, int responseCode, String pMessage) throws RemoteException { handlePreviewCallback(op, responseCode, pMessage); } }; private void handlePreviewCallback(int op, int responseCode, String pMessage) { mPreviewHandler.sendMessage(mPreviewHandler.obtainMessage(op, responseCode, 0, pMessage)); } private String mPDFFileName = null; /** * invoke the pdf viewer to show the downloaded PDF. */ private void startExternalPreviewActivity() { if (hasPreviewLaunched) { return; } hasPreviewLaunched = true; // track the launch of external pdf viewer //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SFM_OPEN_PDF ); BaseApplicationModule.getTrackingModule().trackLink(QBMTrackConstants.SALES_FORM_EMAIL_PAGE_NAME, QBMTrackConstants.SFM_OPEN_PDF); try { mPDFFileName = AppPreferences.getStringPreference(this.getApplicationContext(), BaseAppPreferences.PREFS_QBSHAREDLIB, QBODocumentLinkEntity.KEY_PDF_FILENAME, null); File file = new File(mPDFFileName); Uri fileUri = FileProvider.getUriForFile(this, this.getApplicationContext().getResources().getString(R.string.file_provider_authorities), file); Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(fileUri, this.getContentResolver().getType(fileUri)); startActivity(intent); } catch (final ActivityNotFoundException e) { displayError(R.string.error_pdf_viewer_not_present, R.string.error_title_error, false); } } private void setPDFHeightUsingWidth() { if (mPDFViewCtrl == null) { return; } Configuration configuration = this.getResources().getConfiguration(); final float deviceDensity = getResources().getDisplayMetrics().density; final int screenWidthDp = configuration.screenWidthDp; //The current width of the available screen space, in dp units, corresponding to screen width resource qualifier. final int paddingLeftRight = (int) (getResources().getDimension(R.dimen.sales_form_email_form_padding)); final int pdfContainerWidth = (int) (screenWidthDp * deviceDensity - 2 * paddingLeftRight); LayoutParams layParams = mPDFViewCtrl.getLayoutParams(); layParams.height = (int) (pdfContainerWidth * SalesFormEmail.US_LETTER_HEIGHT_WIDTH_RATIO); mPDFViewCtrl.setLayoutParams(layParams); } /** * invoke the pdf viewer to show the downloaded PDF. */ private void startPreviewActivity() { try { if (mPDFViewCtrl == null) { return; } mPdfLoadingMessage.setVisibility(View.GONE); mPDFViewCtrl.setVisibility(View.VISIBLE); mPDFViewCtrl.setPageViewMode(PDFViewCtrl.PAGE_VIEW_FIT_WIDTH); setPDFHeightUsingWidth(); mPDFFileName = AppPreferences.getStringPreference(this.getApplicationContext(), BaseAppPreferences.PREFS_QBSHAREDLIB, QBODocumentLinkEntity.KEY_PDF_FILENAME, null); File file = new File(mPDFFileName); InputStream in = null; PDFDoc doc; try { in = new BufferedInputStream(new FileInputStream(file)); doc = new PDFDoc(in); mPDFViewCtrl.setDoc(doc); mPDFViewCtrl.setOnClickListener(this); } catch (PDFNetException e) { CustomLog.logDebug(TAG, "startPreviewActivity : PDFNetException"); } catch (IOException e) { CustomLog.logDebug(TAG, "startPreviewActivity : IOException"); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } } catch (final ActivityNotFoundException e) { displayError(R.string.error_pdf_viewer_not_present, R.string.error_title_error, false); } } /** * This method handles the click on the row elements on the include in email screen. * */ @Override public void onClick(View v) { final int anId = v.getId(); if (anId == R.id.pdfviewctrl) { startExternalPreviewActivity(); } } /** * Handle messages being passed from the callback service (it is on a * separate thread loop) in case of PreviewService. * */ private final Handler mPreviewHandler = new PreviewResponseHandler(this) { @Override public void onSuccess() { stopPreviewProgress(); startPreviewActivity(); CustomLog.logDebug(TAG, getClass().getSimpleName() + " : Performance Testing - Preview STOP"); BaseApplicationModule.getTrackingModule().trackEvent(TrackConstants.TRACK_END_EVENT, QBMTrackConstants.SFM_PDF_LOADED, QBMTrackConstants.SUCCESS); // Add success to the flow tracking. //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SFM_PDF_LOADED + QBMTrackConstants.SUCCESS); } @Override public void onFailure(int pStringId, int pDisplayFlag) { stopPreviewProgress(); BaseApplicationModule.getTrackingModule().trackEvent(TrackConstants.TRACK_END_EVENT, QBMTrackConstants.SFM_PDF_LOADED, QBMTrackConstants.ERROR); // Add success to the flow tracking. //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SFM_PDF_LOADED + QBMTrackConstants.ERROR); displayError(pStringId, pDisplayFlag, true); } @Override public void onFailure(int pStringId) { stopPreviewProgress(); BaseApplicationModule.getTrackingModule().trackEvent(TrackConstants.TRACK_END_EVENT, QBMTrackConstants.SFM_PDF_LOADED, QBMTrackConstants.ERROR); // Add success to the flow tracking. //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SFM_PDF_LOADED + QBMTrackConstants.ERROR); displayError(pStringId, R.string.error_title_error, false); } @Override public void onFinish() { try { if (mPreviewServiceConnection != null) { SalesFormEmail.this.unbindService(mPreviewServiceConnection); mPreviewServiceConnection = null; } } catch (final Exception e) { CustomLog.logError(TAG, e, getClass().getSimpleName() + " : Error unbinding Preview service"); } } }; /** * Dismiss the Preview progress dialog */ private void stopPreviewProgress() { if (mProgress != null && mProgress.isShowing()) { mProgress.dismiss(); } if (mPdfLoadingMessage != null) { mPdfLoadingMessage.setVisibility(View.GONE); } } /** * This method handles the data sync response * * @param msg */ private void handleDataSyncResponse(Message msg) { CustomLog.logDebug(TAG, "Sales form email : " + String .format(" Data Service for Sales form email finished with code %s - %s", msg.arg1, msg.obj)); stopProgressBar(); // The doNotFinishActivity flag is set to true used when the error needs to be handled in the same activity. boolean doNotFinishActivity = false; switch (msg.arg1) { case Response.RESPONSE_SUCCESS: if (mCurrentCommandRequest == ISyncConstants.FLAG_QBO_REFERESH_COMPANY_PREFS_ONLY) { doNotFinishActivity = true; // show the popup again. } else { boolean updatePreferenceFailed = "true".equals(msg.obj); if (mCurrentCommandRequest == ISyncConstants.FLAG_EDIT_PREF_AND_SEND_MAIL && !updatePreferenceFailed) { // Upon successful update of preference and sending the email. Update preference to not show OTM again. AppPreferences.setBooleanPreference(QBOApplicationModule.getInstance().getApplicationContext(), null, AppPreferences.KEY_ATTACH_PDF_PREFERENCE_OTM_SHOW, false); } BaseApplicationModule.getTrackingModule().trackEvent(TrackConstants.TRACK_END_EVENT, QBMTrackConstants.SALES_FORM_EMAIL_SENT, QBMTrackConstants.SUCCESS); // Add success to the flow tracking. //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.SUCCESS); if (updatePreferenceFailed) { this.setResult(PSQBOServiceErrorCodes.SALES_FORM_EMAIL_PREFERENCE_UPDATE_ERROR, createIntent((String) msg.obj)); } else { this.setResult(Activity.RESULT_OK, createIntent((String) msg.obj)); } } break; case PSQBOServiceErrorCodes.SALES_FORM_EMAIL_DELIVERY_ERROR: CustomLog.logDebug(TAG, "SalesFormEmail : ERROR detected details in Transaction Payload" + (String) msg.obj); // Add DeliveryError to the flow tracking. // SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.DELIVERY_ERROR); this.setResult(PSQBOServiceErrorCodes.SALES_FORM_EMAIL_DELIVERY_ERROR, createIntent((String) msg.obj)); break; case IResponseCode.STALE_OBJECT_ERROR_VALUE: // Should we have the data sync transaction response handler. CustomLog.logDebug(TAG, "SalesFormEmail : Stale object ERROR detected. Showing the refresh dialog. " + (String) msg.obj); this.setResult(IErrorCodes.ERROR_REPONSE_INVALID_OBJECT_VERSION, createIntent((String) msg.obj)); break; case IErrorCodes.ERROR_REPONSE_INVALID_OBJECT_VERSION: // Should we have the data sync transaction response handler. CustomLog.logDebug(TAG, "SalesFormEmail : Stale object ERROR detected. Showing the refresh dialog. " + (String) msg.obj); refreshPreferencesDialog(); doNotFinishActivity = true; break; default: // Should we have the data sync transaction response handler. CustomLog.logDebug(TAG, "SalesFormEmail : General ERROR detected " + (String) msg.obj); // TODO P1 - we should probably log this in tracking to explore more in this area. // Add Error to the flow tracking. //SalesFormEmail.SFM_Flow_tracking = Util.buildTrackingFlow(SalesFormEmail.SFM_Flow_tracking, QBMTrackConstants.ERROR); this.setResult(PSQBOServiceErrorCodes.SALES_FORM_EMAIL_GENERAL_DELIVERY_ERROR, createIntent((String) msg.obj)); break; } // Post the flow tracking to omniture // BaseApplicationModule.getTrackingModule().trackPropertyValue(SalesFormEmail.SFM_Flow_tracking); // CustomLog.logDebug(TAG, "Build tracking LOG flow and clear it -------- " + SalesFormEmail.SFM_Flow_tracking); if (!doNotFinishActivity) { this.finish(); } } private AlertDialog.Builder mAlertDialogBuilder = null; /* * This method displays a dialog which receives users acknowledgment to refresh preference data. */ private void refreshPreferencesDialog() { if (mProgressDialog != null) { if (mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } } mAlertDialogBuilder = new AlertDialog.Builder(this); mAlertDialogBuilder.setTitle(R.string.new_info_exists) .setMessage(R.string.error_sfm_invalid_preference_version_otm).setIcon(R.drawable.ic_dialog_alert) .setNegativeButton(R.string.refresh, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); refreshPreferenceDataFromServer(); } }).setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { dialog.dismiss(); finish(); } }).show(); } /* * This function gets the latest preference data from the server. */ private void refreshPreferenceDataFromServer() { //BaseApplicationModule.getTrackingModule().trackLink(getOmnitureTrackerValue(QBMTrackConstants.CONTACT_PAGE_NAME), getOmnitureTrackerValue(QBMTrackConstants.REFRESH_DATA_FROM_SERVER)); mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(getString(R.string.preference_refresh)); mProgressDialog.show(); mCurrentCommandRequest = ISyncConstants.FLAG_QBO_REFERESH_COMPANY_PREFS_ONLY; GetCompanyPrefDataRequest classRequest = GetCompanyPrefDataRequest.createRequest(getApplicationContext(), ISyncConstants.FLAG_QBO_REFERESH_COMPANY_PREFS_ONLY, this, this); classRequest.setTag(this); BaseApplicationModule.getNetworkModule().dispatchRequest(classRequest); } /** * Start the progress bar */ private void startProgressBar(String msg) { mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(msg); mProgressDialog.setCancelable(true); mProgressDialog.show(); } /** * Stop the progress bar */ private void stopProgressBar() { try { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); mProgressDialog = null; } } catch (Exception e) { //Absord this as in some cases, the activity is getting destroyed before this is called. If this is the case, we dont need to do //much here. } } @Override protected void onPause() { // This method simply stops the current ongoing rendering thread, text // search thread, and tool super.onPause(); if (mPDFViewCtrl != null) { mPDFViewCtrl.pause(); } stopProgressBar(); } @Override protected void onResume() { // This method simply starts the rendering thread to ensure the PDF // content is available for viewing. super.onResume(); if (mPDFViewCtrl != null) { hasPreviewLaunched = false; mPDFViewCtrl.resume(); } } /** * To delete PDF downloaded by Preview functionality */ private void deletePDF() { if (mPDFFileName != null) { try { final File pdfFile = new File(mPDFFileName); //If pdf file exists delete it from memory if (pdfFile.exists()) { pdfFile.delete(); } } catch (final Exception e) { CustomLog.logError(TAG, e, "Error deleting pdf"); } } } @Override protected void onDestroy() { deletePDF(); if (mPDFViewCtrl != null) { mPDFViewCtrl.destroy(); } // Destroy PDFViewCtrl and clean up memory and used resources. super.onDestroy(); } @Override public void onLowMemory() { // Call this method to lower PDFViewCtrl's memory consumption. super.onLowMemory(); if (mPDFViewCtrl != null) { mPDFViewCtrl.purgeMemory(); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setPDFHeightUsingWidth(); } @Override public void onErrorResponse(VolleyError error) { if (!isFinishing()) { CustomError customError = (CustomError) error; onCallback(customError.getRequestCode(), customError.getErrorCode(), customError.getMessage()); } } @Override public void onResponse(StringResponse response) { if (!isFinishing()) { onCallback(response.requestCode, response.responseCode, response.data); } } }