com.muzima.view.forms.HTMLFormWebViewActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.muzima.view.forms.HTMLFormWebViewActivity.java

Source

/*
 * Copyright (c) 2014. The Trustees of Indiana University.
 *
 * This version of the code is licensed under the MPL 2.0 Open Source license with additional
 * healthcare disclaimer. If the user is an entity intending to commercialize any application
 * that uses this code in a for-profit venture, please contact the copyright holder.
 */

package com.muzima.view.forms;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.ConsoleMessage;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.muzima.MuzimaApplication;
import com.muzima.R;
import com.muzima.api.model.Form;
import com.muzima.api.model.FormData;
import com.muzima.api.model.FormTemplate;
import com.muzima.api.model.Patient;
import com.muzima.api.model.User;
import com.muzima.controller.FormController;
import com.muzima.controller.LocationController;
import com.muzima.controller.ProviderController;
import com.muzima.model.BaseForm;
import com.muzima.model.FormWithData;
import com.muzima.utils.Constants;
import com.muzima.utils.audio.AudioResult;
import com.muzima.utils.barcode.IntentIntegrator;
import com.muzima.utils.barcode.IntentResult;
import com.muzima.utils.imaging.ImageResult;
import com.muzima.utils.video.VideoResult;
import com.muzima.view.BroadcastListenerActivity;
import com.muzima.view.patients.PatientSummaryActivity;
import org.apache.commons.lang.time.DateUtils;
import org.apache.lucene.queryParser.ParseException;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static android.webkit.ConsoleMessage.MessageLevel.ERROR;
import static com.muzima.controller.FormController.FormFetchException;
import static com.muzima.utils.Constants.STATUS_COMPLETE;
import static com.muzima.utils.Constants.STATUS_INCOMPLETE;
import static java.text.MessageFormat.format;

public class HTMLFormWebViewActivity extends BroadcastListenerActivity {
    private static final String TAG = "HTMLFormWebViewActivity";
    public static final String PATIENT = "patient";
    public static final String FORM_INSTANCE = "formInstance";
    public static final String HTML_DATA_STORE = "htmlDataStore";
    public static final String BARCODE = "barCodeComponent";
    public static final String IMAGE = "imagingComponent";
    public static final String AUDIO = "audioComponent";
    public static final String VIDEO = "videoComponent";
    public static final String FORM = "form";
    public static final String DISCRIMINATOR = "discriminator";
    public static final String DEFAULT_AUTO_SAVE_INTERVAL_VALUE_IN_MINS = "2";
    public static final String DEFAULT_FONT_SIZE = "Medium";
    public static final boolean IS_LOGGED_IN_USER_DEFAULT_PROVIDER = false;
    public static final boolean IS_ALLOWED_FORM_DATA_DUPLICATION = true;
    public static final String SAVE_AS_INCOMPLETE = "saveDraft";
    public static final String SAVE_AS_COMPLETED = "submit";

    private WebView webView;
    private Form form;
    private FormTemplate formTemplate;
    private MuzimaProgressDialog progressDialog;
    private FormData formData;
    private Patient patient;
    private BarCodeComponent barCodeComponent;
    private ImagingComponent imagingComponent;
    private AudioComponent audioComponent;
    private VideoComponent videoComponent;
    private Map<String, String> scanResultMap;
    private Map<String, String> imageResultMap;
    private Map<String, String> audioResultMap;
    private Map<String, String> videoResultMap;
    private String sectionName;
    private FormController formController;
    private LocationController locationController;
    private String autoSaveIntervalPreference;
    private boolean encounterProviderPreference;
    private boolean duplicateFormDataPreference;
    final Handler handler = new Handler();
    public String jsonPayload;
    private ProviderController providerController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        formController = ((MuzimaApplication) this.getApplicationContext()).getFormController();
        locationController = ((MuzimaApplication) this.getApplicationContext()).getLocationController();
        providerController = ((MuzimaApplication) this.getApplicationContext()).getProviderController();
        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setDisplayShowHomeEnabled(true);
        actionBar.setDisplayHomeAsUpEnabled(true);
        scanResultMap = new HashMap<String, String>();
        imageResultMap = new HashMap<String, String>();
        audioResultMap = new HashMap<String, String>();
        videoResultMap = new HashMap<String, String>();
        setContentView(R.layout.activity_form_webview);
        progressDialog = new MuzimaProgressDialog(this);
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
        autoSaveIntervalPreference = preferences.getString("autoSaveIntervalPreference",
                DEFAULT_AUTO_SAVE_INTERVAL_VALUE_IN_MINS);
        encounterProviderPreference = preferences.getBoolean("encounterProviderPreference",
                IS_LOGGED_IN_USER_DEFAULT_PROVIDER);
        duplicateFormDataPreference = preferences.getBoolean("duplicateFormDataPreference",
                IS_ALLOWED_FORM_DATA_DUPLICATION);

        showProgressBar("Loading...");
        try {
            setupFormData();
            startAutoSaveProcess();
            setupWebView();
        } catch (FormFetchException e) {
            Log.e(TAG, e.getMessage(), e);
            finish();
        } catch (FormController.FormDataFetchException e) {
            Log.e(TAG, e.getMessage(), e);
            finish();
        } catch (FormController.FormDataSaveException e) {
            Log.e(TAG, e.getMessage(), e);
            finish();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void startAutoSaveProcess() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    webView.loadUrl("javascript:document.autoSaveForm()");
                } catch (Exception e) {
                    Log.e(TAG, "Error while auto saving the form data", e);
                } finally {
                    handler.postDelayed(this,
                            Integer.parseInt(autoSaveIntervalPreference) * DateUtils.MILLIS_PER_MINUTE);
                }
            }
        };
        handler.postDelayed(runnable, Integer.parseInt(autoSaveIntervalPreference) * DateUtils.MILLIS_PER_MINUTE);
    }

    public void stopAutoSaveProcess() {
        handler.removeCallbacksAndMessages(null);
    }

    @Override
    protected void onDestroy() {
        if (progressDialog != null) {
            progressDialog.dismiss();
        }
        stopAutoSaveProcess();
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (isFormComplete() && isEncounterForm()) {
            getSupportMenuInflater().inflate(R.menu.menu_completed_encounter_form, menu);
        } else if (isFormComplete() && !isEncounterForm()) {
            getSupportMenuInflater().inflate(R.menu.menu_completed_registration_form, menu);
        } else {
            getSupportMenuInflater().inflate(R.menu.menu_save_form, menu);
        }
        return true;
    }

    @Override
    protected void onResume() {
        if (scanResultMap != null && !scanResultMap.isEmpty()) {
            String jsonMap = new JSONObject(scanResultMap).toString();
            Log.d(TAG, jsonMap);
            webView.loadUrl("javascript:document.populateBarCode(" + jsonMap + ")");
        }

        if (imageResultMap != null && !imageResultMap.isEmpty()) {
            String jsonMap = new JSONObject(imageResultMap).toString();
            Log.d(TAG, "Header:" + sectionName + "json:" + jsonMap);
            webView.loadUrl("javascript:document.populateImage('" + sectionName + "', " + jsonMap + ")");
        }

        if (audioResultMap != null && !audioResultMap.isEmpty()) {
            String jsonMap = new JSONObject(audioResultMap).toString();
            Log.d(TAG, "Header:" + sectionName + "json:" + jsonMap);
            webView.loadUrl("javascript:document.populateAudio('" + sectionName + "', " + jsonMap + ")");
        }

        if (videoResultMap != null && !videoResultMap.isEmpty()) {
            String jsonMap = new JSONObject(videoResultMap).toString();
            Log.d(TAG, "Header:" + sectionName + "json:" + jsonMap);
            webView.loadUrl("javascript:document.populateVideo('" + sectionName + "', " + jsonMap + ")");
        }

        super.onResume();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.form_save_as_draft:
            try {
                saveDraft();
            } catch (IOException e) {
                Log.e(TAG, "Error while saving the form data", e);
            } catch (JSONException e) {
                Log.e(TAG, "Error while saving the form data", e);
            } catch (InterruptedException e) {
                Log.e(TAG, "Error while saving the form data", e);
            }
            return true;
        case R.id.form_submit:
            try {
                saveCompleted();
            } catch (IOException e) {
                Log.e(TAG, "Error while saving the form data", e);
            } catch (JSONException e) {
                Log.e(TAG, "Error while saving the form data", e);
            } catch (InterruptedException e) {
                Log.e(TAG, "Error while saving the form data", e);
            }
            return true;
        case R.id.form_close:
            processBackButtonPressed();
            return true;
        case android.R.id.home:
            showAlertDialog();
            return true;
        case R.id.form_back_to_draft:
            try {
                formData.setStatus(STATUS_INCOMPLETE);
                formController.saveFormData(formData);
            } catch (FormController.FormDataSaveException e) {
                Log.e(TAG, "Error while saving the form data", e);
            }
            startIncompleteFormListActivity();
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void showAlertDialog() {
        new AlertDialog.Builder(HTMLFormWebViewActivity.this).setCancelable(true)
                .setIcon(getResources().getDrawable(R.drawable.ic_warning))
                .setTitle(getResources().getString(R.string.caution))
                .setMessage(getResources().getString(R.string.exit_form_message))
                .setPositiveButton(getString(R.string.yes_button_label), positiveClickListener())
                .setNegativeButton(getString(R.string.no_button_label), null).create().show();
    }

    public void showWarningDialog(String saveType) {
        new AlertDialog.Builder(HTMLFormWebViewActivity.this).setCancelable(true)
                .setIcon(getResources().getDrawable(R.drawable.ic_warning))
                .setTitle(getResources().getString(R.string.already_exists_form_title))
                .setMessage(getResources().getString(R.string.already_exists_form_message))
                .setPositiveButton(getString(R.string.duplicate_form_button_label),
                        duplicateFormDataClickListener(saveType))
                .setNegativeButton(getString(R.string.cancel), null).create().show();
    }

    public void showWarningDialog() {
        new AlertDialog.Builder(HTMLFormWebViewActivity.this).setCancelable(true)
                .setIcon(getResources().getDrawable(R.drawable.ic_warning))
                .setTitle(getResources().getString(R.string.already_exists_form_title))
                .setMessage(getResources().getString(R.string.already_exists_form_message))
                .setNegativeButton(getString(R.string.alert_Ok), null).create().show();
    }

    private boolean isFormAlreadyExist() throws IOException, JSONException {
        return formController.isFormAlreadyExist(this.jsonPayload, this.formData);
    }

    private Dialog.OnClickListener duplicateFormDataClickListener(final String saveType) {

        return new Dialog.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (saveType.equals(SAVE_AS_INCOMPLETE)) {
                    webView.loadUrl("javascript:document.saveDraft()");
                } else if (saveType.equals(SAVE_AS_COMPLETED)) {
                    webView.loadUrl("javascript:document.submit()");
                }
            }
        };
    }

    public void saveDraft() throws IOException, JSONException, InterruptedException {
        loadJson();
        Thread.sleep(500);
        if (!isFormComplete()) {
            if (isFormAlreadyExist() && duplicateFormDataPreference) {
                showWarningDialog(SAVE_AS_INCOMPLETE);
            } else {
                webView.loadUrl("javascript:document.saveDraft()");
            }
        }
    }

    public void saveCompleted() throws IOException, JSONException, InterruptedException {
        loadJson();
        Thread.sleep(500);
        if (isFormAlreadyExist() && duplicateFormDataPreference) {
            showWarningDialog(SAVE_AS_COMPLETED);
        } else {
            webView.loadUrl("javascript:document.submit()");
        }
    }

    private void loadJson() {
        webView.loadUrl("javascript:document.loadJson()");
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
        if (scanResult != null && barCodeComponent.getFieldName() != null && scanResult.getContents() != null) {
            scanResultMap.put(barCodeComponent.getFieldName(), scanResult.getContents());
        }

        ImageResult imageResult = ImagingComponent.parseActivityResult(requestCode, resultCode, intent);
        if (imageResult != null) {
            sectionName = imageResult.getSectionName();
            imageResultMap.put(imagingComponent.getImagePathField(), imageResult.getImageUri());
            imageResultMap.put(imagingComponent.getImageCaptionField(), imageResult.getImageCaption());
        }

        AudioResult audioResult = AudioComponent.parseActivityResult(requestCode, resultCode, intent);
        if (audioResult != null) {
            sectionName = audioResult.getSectionName();
            audioResultMap.put(audioComponent.getAudioPathField(), audioResult.getAudioUri());
            audioResultMap.put(audioComponent.getAudioCaptionField(), audioResult.getAudioCaption());
        }

        VideoResult videoResult = VideoComponent.parseActivityResult(requestCode, resultCode, intent);
        if (videoResult != null) {
            sectionName = videoResult.getSectionName();
            videoResultMap.put(videoComponent.getVideoPathField(), videoResult.getVideoUri());
            videoResultMap.put(videoComponent.getVideoCaptionField(), videoResult.getVideoCaption());
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            showAlertDialog();
            return false;
        }
        return super.onKeyDown(keyCode, event);
    }

    public void startPatientSummaryView(Patient patient) {
        Intent intent = new Intent(this, PatientSummaryActivity.class);
        intent.putExtra(PatientSummaryActivity.PATIENT, patient);
        startActivity(intent);
    }

    public void startIncompleteFormListActivity() {
        startActivity(new Intent(this, FormsActivity.class));
    }

    private boolean isFormComplete() {
        return formData.getStatus().equalsIgnoreCase(STATUS_COMPLETE);
    }

    private void setupFormData() throws FormFetchException, FormController.FormDataFetchException,
            FormController.FormDataSaveException, IOException, ParseException {
        FormController formController = ((MuzimaApplication) getApplication()).getFormController();
        BaseForm baseForm = (BaseForm) getIntent().getSerializableExtra(FORM);
        form = formController.getFormByUuid(baseForm.getFormUuid());
        patient = (Patient) getIntent().getSerializableExtra(PATIENT);
        formTemplate = formController.getFormTemplateByUuid(baseForm.getFormUuid());

        if (baseForm.hasData()) {
            formData = formController.getFormDataByUuid(((FormWithData) baseForm).getFormDataUuid());
        } else {
            formData = createNewFormData();
        }
    }

    private FormData createNewFormData() throws FormController.FormDataSaveException, IOException, ParseException {
        FormData formData = new FormData() {
            {
                setUuid(UUID.randomUUID().toString());
                setPatientUuid(patient.getUuid());
                setUserUuid("userUuid");
                setStatus(STATUS_INCOMPLETE);
                setTemplateUuid(form.getUuid());
                setDiscriminator(form.getDiscriminator());
            }
        };
        User user = ((MuzimaApplication) getApplicationContext()).getAuthenticatedUser();

        formData.setJsonPayload(
                new HTMLPatientJSONMapper().map(patient, formData, user, encounterProviderPreference));
        return formData;
    }

    private void setupWebView() {
        webView = (WebView) findViewById(R.id.webView);
        webView.setWebChromeClient(createWebChromeClient());

        getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
        getSettings().setJavaScriptEnabled(true);
        getSettings().setDatabaseEnabled(true);
        getSettings().setDomStorageEnabled(true);
        getSettings().setBuiltInZoomControls(true);

        FormInstance formInstance = new FormInstance(form, formTemplate);
        webView.addJavascriptInterface(formInstance, FORM_INSTANCE);
        barCodeComponent = new BarCodeComponent(this);
        imagingComponent = new ImagingComponent(this);
        audioComponent = new AudioComponent(this);
        videoComponent = new VideoComponent(this);
        webView.addJavascriptInterface(barCodeComponent, BARCODE);
        webView.addJavascriptInterface(imagingComponent, IMAGE);
        webView.addJavascriptInterface(audioComponent, AUDIO);
        webView.addJavascriptInterface(videoComponent, VIDEO);
        webView.addJavascriptInterface(
                new HTMLFormDataStore(this, formController, locationController, formData, providerController),
                HTML_DATA_STORE);
        webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
        if (isFormComplete()) {
            webView.setOnTouchListener(createCompleteFormListenerToDisableInput());
        }
        webView.loadDataWithBaseURL("file:///android_asset/www/forms/", prePopulateData(), "text/html", "UTF-8",
                "");
    }

    private String prePopulateData() {
        if (formData.getJsonPayload() == null) {
            return formTemplate.getHtml();
        }
        Document document = Jsoup.parse(formTemplate.getHtml());
        String json = formData.getJsonPayload();
        String htmlWithJSON = "<div id='pre_populate_data'>" + json + "</div>";
        document.select("body").prepend(htmlWithJSON);
        return document.toString();
    }

    private WebChromeClient createWebChromeClient() {
        return new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int progress) {
                HTMLFormWebViewActivity.this.setProgress(progress * 1000);
                if (progress == 100) {
                    progressDialog.dismiss();
                }
            }

            @Override
            public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
                String message = format("Javascript Log. Message: {0}, lineNumber: {1}, sourceId, {2}",
                        consoleMessage.message(), consoleMessage.lineNumber(), consoleMessage.sourceId());
                if (consoleMessage.messageLevel() == ERROR) {
                    Log.e(TAG, message);
                } else {
                    Log.d(TAG, message);
                }
                return true;
            }
        };
    }

    private View.OnTouchListener createCompleteFormListenerToDisableInput() {
        return new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() != MotionEvent.ACTION_MOVE) {
                    view.setFocusable(false);
                    view.setEnabled(false);
                    return true;
                }
                return false;
            }
        };
    }

    private WebSettings getSettings() {
        return webView.getSettings();
    }

    private Dialog.OnClickListener positiveClickListener() {
        return new Dialog.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                processBackButtonPressed();
            }
        };
    }

    private void processBackButtonPressed() {
        handler.removeCallbacksAndMessages(null);
        onBackPressed();
    }

    public void showProgressBar(final String message) {
        runOnUiThread(new Runnable() {
            public void run() {
                progressDialog.show(message);
            }
        });
    }

    private boolean isEncounterForm() {
        return formData.getDiscriminator().equalsIgnoreCase(Constants.FORM_JSON_DISCRIMINATOR_ENCOUNTER)
                || formData.getDiscriminator().equalsIgnoreCase(Constants.FORM_JSON_DISCRIMINATOR_CONSULTATION);
    }

    public Handler getHandler() {
        return handler;
    }
}