com.renard.ocr.documents.creation.NewDocumentActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.renard.ocr.documents.creation.NewDocumentActivity.java

Source

/*
 * Copyright (C) 2012,2013 Renard Wellnitz.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.renard.ocr.documents.creation;

import com.crashlytics.android.Crashlytics;
import com.renard.ocr.MonitoredActivity;
import com.renard.ocr.R;
import com.renard.ocr.TextFairyApplication;
import com.renard.ocr.documents.creation.crop.CropImageActivity;
import com.renard.ocr.documents.creation.visualisation.OCRActivity;
import com.renard.ocr.documents.viewing.DocumentContentProvider;
import com.renard.ocr.documents.viewing.DocumentContentProvider.Columns;
import com.renard.ocr.documents.viewing.grid.DocumentGridActivity;
import com.renard.ocr.documents.viewing.single.DocumentActivity;
import com.renard.ocr.pdf.Hocr2Pdf;
import com.renard.ocr.pdf.Hocr2Pdf.PDFProgressListener;
import com.renard.ocr.util.MemoryInfo;
import com.renard.ocr.util.Util;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.accessibility.AccessibilityManagerCompat;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * activities which extend this activity can create a new document. this class
 * also containes the code for functionality which is shared by
 * {@link DocumentGridActivity} and {@link DocumentActivity}
 *
 * @author renard
 */
public abstract class NewDocumentActivity extends MonitoredActivity {

    private final static String LOG_TAG = NewDocumentActivity.class.getSimpleName();
    public final static String EXTRA_NATIVE_PIX = "pix_pointer";
    private final static String IMAGE_LOAD_PROGRESS_TAG = "image_load_progress";

    private static final int PDF_PROGRESS_DIALOG_ID = 0;
    private static final int DELETE_PROGRESS_DIALOG_ID = 1;
    protected static final int HINT_DIALOG_ID = 2;
    private static final int EDIT_TITLE_DIALOG_ID = 3;

    private static final String DIALOG_ARG_MAX = "max";
    private static final String DIALOG_ARG_MESSAGE = "message";
    private static final String DIALOG_ARG_PROGRESS = "progress";
    private static final String DIALOG_ARG_SECONDARY_PROGRESS = "secondary_progress";
    private static final String DIALOG_ARG_TITLE = "title";
    private static final String DIALOG_ARG_DOCUMENT_URI = "document_uri";

    private final static int REQUEST_CODE_MAKE_PHOTO = 0;
    private final static int REQUEST_CODE_PICK_PHOTO = 1;
    final static int REQUEST_CODE_CROP_PHOTO = 2;
    protected final static int REQUEST_CODE_OCR = 3;

    private static final String DATE_CAMERA_INTENT_STARTED_STATE = "com.renard.ocr.android.photo.TakePhotoActivity.dateCameraIntentStarted";
    private static final String STATE_RECEIVER_REGISTERED = "state_receiver_registered";
    private static final String IMAGE_SOURCE = "image_source";
    private static Date dateCameraIntentStarted = null;
    private static final String CAMERA_PIC_URI_STATE = "com.renard.ocr.android.photo.TakePhotoActivity.CAMERA_PIC_URI_STATE";
    private static Uri cameraPicUri = null;
    private boolean mReceiverRegistered = false;
    private ImageSource mImageSource = ImageSource.CAMERA;

    private static class CameraResult {
        public CameraResult(int requestCode, int resultCode, Intent data, ImageSource source) {
            mRequestCode = requestCode;
            mResultCode = resultCode;
            mData = data;
            mSource = source;
        }

        private int mRequestCode;
        private int mResultCode;
        private Intent mData;
        private final ImageSource mSource;
    }

    protected abstract int getParentId();

    private ProgressDialog pdfProgressDialog;
    private ProgressDialog deleteProgressDialog;
    private AsyncTask<Void, Void, ImageLoadAsyncTask.LoadResult> mBitmapLoadTask;
    private CameraResult mCameraResult;

    private void checkRam(MemoryWarningDialog.DoAfter doAfter) {

        long availableMegs = MemoryInfo.getFreeMemory(this);
        Log.i(LOG_TAG, "available ram = " + availableMegs);
        if (availableMegs < MemoryInfo.MINIMUM_RECOMMENDED_RAM) {
            MemoryWarningDialog.newInstance(availableMegs, doAfter).show(getSupportFragmentManager(),
                    MemoryWarningDialog.TAG);
        } else if (doAfter == MemoryWarningDialog.DoAfter.START_CAMERA) {
            startCamera();
        } else if (doAfter == MemoryWarningDialog.DoAfter.START_GALLERY) {
            startGallery();
        }
    }

    protected void startGallery() {
        mAnalytics.startGallery();
        cameraPicUri = null;
        Intent i;
        if (Build.VERSION.SDK_INT >= 19) {
            i = new Intent(Intent.ACTION_GET_CONTENT, null);
            i.addCategory(Intent.CATEGORY_OPENABLE);
            i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            i.setType("image/*");
            i.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "image/png", "image/jpg", "image/jpeg" });
        } else {
            i = new Intent(Intent.ACTION_GET_CONTENT, null);
            i.setType("image/png,image/jpg, image/jpeg");
        }

        Intent chooser = Intent.createChooser(i, getString(R.string.image_source));
        try {
            startActivityForResult(chooser, REQUEST_CODE_PICK_PHOTO);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.no_gallery_found, Toast.LENGTH_LONG).show();
        }
    }

    protected void startCamera() {
        mAnalytics.startCamera();
        try {
            cameraPicUri = null;
            dateCameraIntentStarted = new Date();
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            // Create an image file name
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
            String imageFileName = "JPEG_" + timeStamp + "_";

            File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
            File image;
            try {
                if (!storageDir.exists()) {
                    storageDir.mkdirs();
                }
                image = new File(storageDir, imageFileName + ".jpg");
                if (image.exists()) {
                    image.createNewFile();
                }
                cameraPicUri = Uri.fromFile(image);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraPicUri);
                startActivityForResult(intent, REQUEST_CODE_MAKE_PHOTO);
            } catch (IOException e) {
                showFileError(PixLoadStatus.IO_ERROR);
            }

        } catch (ActivityNotFoundException e) {
            showFileError(PixLoadStatus.CAMERA_APP_NOT_FOUND);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        Log.i(LOG_TAG, "onSaveInstanceState" + this);
        //remember to register the receiver again in #onRestoreInstanceState
        savedInstanceState.putBoolean(STATE_RECEIVER_REGISTERED, mReceiverRegistered);
        unRegisterImageLoadedReceiver();
        //unregister receiver before onSaveInstanceState is called!
        super.onSaveInstanceState(savedInstanceState);
        if (dateCameraIntentStarted != null) {
            savedInstanceState.putLong(DATE_CAMERA_INTENT_STARTED_STATE, dateCameraIntentStarted.getTime());
        }
        if (cameraPicUri != null) {
            savedInstanceState.putString(CAMERA_PIC_URI_STATE, cameraPicUri.toString());
        }
        savedInstanceState.putInt(IMAGE_SOURCE, mImageSource.ordinal());
    }

    @TargetApi(11)
    @Override
    protected synchronized void onDestroy() {
        super.onDestroy();
        unRegisterImageLoadedReceiver();
        //cancel loading of image if the activity is destroyed for good
        if (android.os.Build.VERSION.SDK_INT >= 11 && !isChangingConfigurations() && mBitmapLoadTask != null) {
            mBitmapLoadTask.cancel(false);
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.i(LOG_TAG, "onRestoreInstanceState " + this);
        super.onRestoreInstanceState(savedInstanceState);

        if (savedInstanceState.containsKey(DATE_CAMERA_INTENT_STARTED_STATE)) {
            dateCameraIntentStarted = new Date(savedInstanceState.getLong(DATE_CAMERA_INTENT_STARTED_STATE));
        }
        if (savedInstanceState.containsKey(CAMERA_PIC_URI_STATE)) {
            cameraPicUri = Uri.parse(savedInstanceState.getString(CAMERA_PIC_URI_STATE));
        }
        if (savedInstanceState.getBoolean(STATE_RECEIVER_REGISTERED)) {
            registerImageLoaderReceiver();
        }
        final int index = savedInstanceState.getInt(IMAGE_SOURCE);
        mImageSource = ImageSource.values()[index];
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int itemId = item.getItemId();
        if (itemId == R.id.item_camera) {
            checkRam(MemoryWarningDialog.DoAfter.START_CAMERA);
            return true;
        } else if (itemId == R.id.item_gallery) {
            checkRam(MemoryWarningDialog.DoAfter.START_GALLERY);
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.base_document_activity_options, menu);
        return true;
    }

    private void onTakePhotoActivityResult(CameraResult cameraResult) {
        if (cameraResult.mResultCode == RESULT_OK) {
            if (cameraResult.mRequestCode == REQUEST_CODE_MAKE_PHOTO) {
                Cursor myCursor = null;
                Date dateOfPicture;
                //check if there is a file at the uri we specified
                if (cameraPicUri != null) {
                    File f = new File(cameraPicUri.getPath());
                    if (f.isFile() && f.exists() && f.canRead()) {
                        //all is well
                        Log.i(LOG_TAG, "onTakePhotoActivityResult");
                        loadBitmapFromContentUri(cameraPicUri, ImageSource.CAMERA);
                        return;
                    }

                }
                //try to look up the image by querying the media content provider
                try {
                    // Create a Cursor to obtain the file Path for the large
                    // image
                    String[] largeFileProjection = { MediaStore.Images.ImageColumns._ID,
                            MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.ORIENTATION,
                            MediaStore.Images.ImageColumns.DATE_TAKEN };
                    String largeFileSort = MediaStore.Images.ImageColumns._ID + " DESC";
                    myCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            largeFileProjection, null, null, largeFileSort);
                    if (myCursor != null) {
                        myCursor.moveToFirst();
                        // This will actually give you the file path location of the
                        // image.
                        String largeImagePath = myCursor
                                .getString(myCursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATA));
                        Uri tempCameraPicUri = Uri.fromFile(new File(largeImagePath));
                        dateOfPicture = new Date(myCursor.getLong(
                                myCursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATE_TAKEN)));
                        if (dateOfPicture.getTime() == 0 || (dateOfPicture.after(dateCameraIntentStarted))) {
                            cameraPicUri = tempCameraPicUri;
                        }
                    }
                } catch (Exception ignored) {
                } finally {
                    if (myCursor != null) {
                        myCursor.close();
                    }
                }
            }

            if (cameraPicUri == null) {
                try {
                    cameraPicUri = mCameraResult.mData.getData();
                } catch (Exception e) {
                    showFileError(PixLoadStatus.CAMERA_APP_ERROR);
                }
            }

            if (cameraPicUri != null) {
                loadBitmapFromContentUri(cameraPicUri, mCameraResult.mSource);
            } else {
                showFileError(PixLoadStatus.CAMERA_NO_IMAGE_RETURNED);
            }
        }
    }

    protected void loadBitmapFromContentUri(final Uri cameraPicUri, ImageSource source) {
        if (TextFairyApplication.isRelease()) {
            Crashlytics.log("Loading " + cameraPicUri.toString() + " from " + source.name());
        }
        mImageSource = source;
        if (mBitmapLoadTask != null) {
            mBitmapLoadTask.cancel(true);
        }
        AccessibilityManager am = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
        boolean isAccessibilityEnabled = am.isEnabled();
        boolean isExploreByTouchEnabled = AccessibilityManagerCompat.isTouchExplorationEnabled(am);
        final boolean skipCrop = isExploreByTouchEnabled && isAccessibilityEnabled;

        registerImageLoaderReceiver();
        mBitmapLoadTask = new ImageLoadAsyncTask(this, skipCrop, cameraPicUri).execute();

    }

    private synchronized void unRegisterImageLoadedReceiver() {
        if (mReceiverRegistered) {
            Log.i(LOG_TAG, "unRegisterImageLoadedReceiver " + mMessageReceiver);
            LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
            mReceiverRegistered = false;
        }
    }

    private synchronized void registerImageLoaderReceiver() {
        if (!mReceiverRegistered) {
            Log.i(LOG_TAG, "registerImageLoaderReceiver " + mMessageReceiver);
            final IntentFilter intentFilter = new IntentFilter(ImageLoadAsyncTask.ACTION_IMAGE_LOADED);
            intentFilter.addAction(ImageLoadAsyncTask.ACTION_IMAGE_LOADING_START);
            LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, intentFilter);
            mReceiverRegistered = true;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (RESULT_OK == resultCode) {
            switch (requestCode) {
            case REQUEST_CODE_CROP_PHOTO: {
                long nativePix = data.getLongExtra(EXTRA_NATIVE_PIX, 0);
                startOcrActivity(nativePix, false);
                break;
            }
            case REQUEST_CODE_MAKE_PHOTO:
                mCameraResult = new CameraResult(requestCode, resultCode, data, ImageSource.CAMERA);
                break;
            case REQUEST_CODE_PICK_PHOTO:
                mCameraResult = new CameraResult(requestCode, resultCode, data, ImageSource.PICK);
                break;
            }
        } else if (CropImageActivity.RESULT_NEW_IMAGE == resultCode) {
            switch (mImageSource) {
            case PICK:
                startGallery();
                break;
            case INTENT:
                break;
            case CAMERA:
                startCamera();
                break;
            }

        }
    }

    void startOcrActivity(long nativePix, boolean accessibilityMode) {
        Intent intent = new Intent(this, OCRActivity.class);
        intent.putExtra(EXTRA_NATIVE_PIX, nativePix);
        intent.putExtra(OCRActivity.EXTRA_USE_ACCESSIBILITY_MODE, accessibilityMode);
        intent.putExtra(OCRActivity.EXTRA_PARENT_DOCUMENT_ID, getParentId());
        startActivityForResult(intent, REQUEST_CODE_OCR);
    }

    @Override
    protected void onResumeFragments() {
        super.onResumeFragments();
        if (mCameraResult != null) {
            onTakePhotoActivityResult(mCameraResult);
            mCameraResult = null;
        }
    }

    // handler for received Intents for the image loaded event
    private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //I get quite a number of crash reports here saying that I cannot show a dialog after onSaveInstanceState.
            //However the broadcastReceiver gets unregistered in onSaveInstanceState before i call super().
            //As a workaround I check for the flag if the receiver is registered
            //Additionally i use commitAllowStateLoss as its not terribly important to preserve the state of the loading dialog
            if (mReceiverRegistered) {
                Log.i(LOG_TAG, "onReceive " + NewDocumentActivity.this);
                if (intent.getAction().equalsIgnoreCase(ImageLoadAsyncTask.ACTION_IMAGE_LOADED)) {
                    unRegisterImageLoadedReceiver();
                    final long nativePix = intent.getLongExtra(ImageLoadAsyncTask.EXTRA_PIX, 0);
                    final int statusNumber = intent.getIntExtra(ImageLoadAsyncTask.EXTRA_STATUS,
                            PixLoadStatus.SUCCESS.ordinal());
                    final boolean skipCrop = intent.getBooleanExtra(ImageLoadAsyncTask.EXTRA_SKIP_CROP, false);
                    handleLoadedImage(nativePix, PixLoadStatus.values()[statusNumber], skipCrop);
                } else if (intent.getAction().equalsIgnoreCase(ImageLoadAsyncTask.ACTION_IMAGE_LOADING_START)) {
                    showLoadingImageProgressDialog();
                }
            }
        }
    };

    private void handleLoadedImage(long nativePix, PixLoadStatus pixLoadStatus, boolean skipCrop) {
        dismissLoadingImageProgressDialog();

        if (pixLoadStatus == PixLoadStatus.SUCCESS) {
            if (skipCrop) {
                startOcrActivity(nativePix, true);
            } else {
                Intent actionIntent = new Intent(this, CropImageActivity.class);
                actionIntent.putExtra(NewDocumentActivity.EXTRA_NATIVE_PIX, nativePix);
                startActivityForResult(actionIntent, NewDocumentActivity.REQUEST_CODE_CROP_PHOTO);
            }
        } else {
            showFileError(pixLoadStatus);
        }
    }

    private void dismissLoadingImageProgressDialog() {
        Fragment prev = getSupportFragmentManager().findFragmentByTag(IMAGE_LOAD_PROGRESS_TAG);
        if (prev != null) {
            Log.i(LOG_TAG, "dismissing dialog");
            DialogFragment df = (DialogFragment) prev;
            df.dismissAllowingStateLoss();
        } else {
            Log.i(LOG_TAG, "cannot dismiss dialog. its null! " + this);
        }
    }

    private void showLoadingImageProgressDialog() {
        Log.i(LOG_TAG, "showLoadingImageProgressDialog");
        //dialog.show(getSupportFragmentManager(), null);
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        final ProgressDialogFragment dialog = ProgressDialogFragment.newInstance(R.string.please_wait,
                R.string.loading_image);
        ft.add(dialog, IMAGE_LOAD_PROGRESS_TAG);
        ft.commitAllowingStateLoss();
    }

    void showFileError(PixLoadStatus status) {
        showFileError(status, null);
    }

    protected void showFileError(PixLoadStatus second, OnClickListener positiveListener) {
        int textId;
        switch (second) {
        case IMAGE_NOT_32_BIT:
            textId = R.string.image_not_32_bit;
            break;
        case IMAGE_FORMAT_UNSUPPORTED:
            textId = R.string.image_format_unsupported;
            break;
        case IMAGE_COULD_NOT_BE_READ:
            textId = R.string.image_could_not_be_read;
            break;
        case IMAGE_DOES_NOT_EXIST:
            textId = R.string.image_does_not_exist;
            break;
        case IO_ERROR:
            textId = R.string.gallery_io_error;
            break;
        case CAMERA_APP_NOT_FOUND:
            textId = R.string.camera_app_not_found;
            break;
        case MEDIA_STORE_RETURNED_NULL:
            textId = R.string.media_store_returned_null;
            break;
        case CAMERA_APP_ERROR:
            textId = R.string.camera_app_error;
            break;
        case CAMERA_NO_IMAGE_RETURNED:
            textId = R.string.camera_no_image_returned;
            break;
        default:
            textId = R.string.error_could_not_take_photo;
        }

        AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle(R.string.error_title);
        final TextView textview = new TextView(this);
        textview.setText(textId);
        alert.setView(textview);
        alert.setPositiveButton(android.R.string.ok, positiveListener);
        alert.show();
    }

    @Override
    protected Dialog onCreateDialog(int id, Bundle args) {
        switch (id) {

        case PDF_PROGRESS_DIALOG_ID:
            int max = args.getInt(DIALOG_ARG_MAX);
            String message = args.getString(DIALOG_ARG_MESSAGE);
            String title = args.getString(DIALOG_ARG_TITLE);
            pdfProgressDialog = new ProgressDialog(this);
            pdfProgressDialog.setMessage(message);
            pdfProgressDialog.setTitle(title);
            pdfProgressDialog.setIndeterminate(false);
            pdfProgressDialog.setMax(max);
            pdfProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            pdfProgressDialog.setCancelable(false);
            return pdfProgressDialog;
        case DELETE_PROGRESS_DIALOG_ID:
            max = args.getInt(DIALOG_ARG_MAX);
            message = args.getString(DIALOG_ARG_MESSAGE);
            deleteProgressDialog = new ProgressDialog(this);
            deleteProgressDialog.setMessage(message);
            deleteProgressDialog.setIndeterminate(false);
            deleteProgressDialog.setMax(max);
            deleteProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            deleteProgressDialog.setCancelable(false);
            return deleteProgressDialog;
        case EDIT_TITLE_DIALOG_ID:
            View layout = getLayoutInflater().inflate(R.layout.edit_title_dialog, null);
            final Uri documentUri = Uri.parse(args.getString(DIALOG_ARG_DOCUMENT_URI));
            final String oldTitle = args.getString(DIALOG_ARG_TITLE);
            final EditText edit = (EditText) layout.findViewById(R.id.edit_title);
            edit.setText(oldTitle);

            AlertDialog.Builder builder = new Builder(this);
            builder.setView(layout);
            builder.setTitle(R.string.edit_dialog_title);
            builder.setIcon(R.drawable.fairy_showing);
            builder.setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    final String title = edit.getText().toString();
                    saveTitle(title, documentUri);

                }
            });
            builder.setNegativeButton(R.string.cancel, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {

                }
            });
            builder.show();
        }
        return super.onCreateDialog(id, args);
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
        switch (id) {
        case EDIT_TITLE_DIALOG_ID:
            final Uri documentUri = Uri.parse(args.getString(DIALOG_ARG_DOCUMENT_URI));
            final String oldTitle = args.getString(DIALOG_ARG_TITLE);
            final EditText edit = (EditText) dialog.findViewById(R.id.edit_title);
            edit.setText(oldTitle);
            AlertDialog alertDialog = (AlertDialog) dialog;
            Button okButton = alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL);
            okButton.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    final String title = edit.getText().toString();
                    saveTitle(title, documentUri);
                }
            });
            break;
        case HINT_DIALOG_ID:
            break;
        default:
            if (args != null) {
                final int max = args.getInt(DIALOG_ARG_MAX);
                final int progress = args.getInt(DIALOG_ARG_PROGRESS);
                // final int secondaryProgress =
                // args.getInt(DIALOG_ARG_SECONDARY_PROGRESS);
                final String message = args.getString(DIALOG_ARG_MESSAGE);
                final String title = args.getString(DIALOG_ARG_TITLE);
                if (id == PDF_PROGRESS_DIALOG_ID) {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            pdfProgressDialog.setProgress(progress);
                            pdfProgressDialog.setMax(max);
                            if (message != null) {
                                pdfProgressDialog.setMessage(message);
                            }
                            if (title != null) {
                                pdfProgressDialog.setTitle(title);
                            }
                        }
                    });

                } else if (id == DELETE_PROGRESS_DIALOG_ID) {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            deleteProgressDialog.setProgress(progress);
                            deleteProgressDialog.setMax(max);
                            if (message != null) {
                                deleteProgressDialog.setMessage(message);
                            }
                        }
                    });
                }
            }
        }
        super.onPrepareDialog(id, dialog, args);
    }

    protected void askUserForNewTitle(final String oldTitle, final Uri documentUri) {
        Bundle bundle = new Bundle(2);
        bundle.putString(DIALOG_ARG_TITLE, oldTitle);
        bundle.putString(DIALOG_ARG_DOCUMENT_URI, documentUri.toString());
        showDialog(EDIT_TITLE_DIALOG_ID, bundle);
    }

    private void saveTitle(final String newTitle, final Uri documentUri) {
        Uri uri = documentUri;
        if (uri == null) {
            uri = getIntent().getData();
        }
        if (uri != null) {
            SaveDocumentTask saveTask = new SaveDocumentTask(this, documentUri, newTitle);
            saveTask.execute();
        }

    }

    /**
     * *******************************************
     * <p/>
     * ASYNC TASKS
     */

    protected class CreatePDFTask extends AsyncTask<Void, Integer, Pair<ArrayList<Uri>, ArrayList<Uri>>>
            implements PDFProgressListener {

        private Set<Integer> mIds = new HashSet<Integer>();
        private int mCurrentPageCount;
        private int mCurrentDocumentIndex;
        private String mCurrentDocumentName;
        private StringBuilder mOCRText = new StringBuilder();

        public CreatePDFTask(Set<Integer> ids) {
            mIds.addAll(ids);
        }

        @Override
        public void onNewPage(int pageNumber) {
            Bundle args = new Bundle(5);

            String progressMsg = getResources().getString(R.string.progress_pfd_creation);
            progressMsg = String.format(progressMsg, pageNumber, mCurrentPageCount, mCurrentDocumentName);

            String title = getResources().getString(R.string.pdf_creation_message);

            args.putString(DIALOG_ARG_MESSAGE, title);
            args.putString(DIALOG_ARG_MESSAGE, progressMsg);
            args.putInt(DIALOG_ARG_MAX, mIds.size());
            args.putInt(DIALOG_ARG_PROGRESS, mCurrentDocumentIndex);
            args.putInt(DIALOG_ARG_SECONDARY_PROGRESS, pageNumber);
            showDialog(PDF_PROGRESS_DIALOG_ID, args);
        }

        @Override
        protected void onPreExecute() {
            mOCRText.delete(0, mOCRText.length());
            Bundle args = new Bundle(2);
            args.putInt(DIALOG_ARG_MAX, mIds.size());
            args.putInt(DIALOG_ARG_PROGRESS, 0);
            String message = getText(R.string.pdf_creation_message).toString();
            args.putString(DIALOG_ARG_MESSAGE, message);
            showDialog(PDF_PROGRESS_DIALOG_ID, args);
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Pair<ArrayList<Uri>, ArrayList<Uri>> files) {
            dismissDialog(PDF_PROGRESS_DIALOG_ID);
            if (files != null && files.first.size() > 0) {
                if (files.first.size() > 1) {
                    //we have more than one pdf file
                    //share by sending them
                    sharePDFBySending(files);
                } else {
                    // single pdf file
                    // share by opening pdf viewer
                    Intent target = new Intent(Intent.ACTION_VIEW);
                    target.setDataAndType(files.first.get(0), "application/pdf");
                    target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

                    Intent intent = Intent.createChooser(target, "Open File");
                    try {
                        startActivity(intent);
                    } catch (ActivityNotFoundException e) {
                        sharePDFBySending(files);
                    }

                }

            } else {
                Toast.makeText(getApplicationContext(), getText(R.string.error_create_file), Toast.LENGTH_LONG)
                        .show();
            }
        }

        private void sharePDFBySending(Pair<ArrayList<Uri>, ArrayList<Uri>> files) {
            Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);
            shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getText(R.string.share_subject));
            CharSequence seq = Html.fromHtml(mOCRText.toString());
            shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, seq);
            shareIntent.setType("application/pdf");
            ArrayList<Uri> allFiles = new ArrayList<Uri>();
            allFiles.addAll(files.first);
            allFiles.addAll(files.second);
            shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, allFiles);
            startActivity(Intent.createChooser(shareIntent, getText(R.string.share_chooser_title)));
        }

        private Pair<File, File> createPDF(File dir, long documentId) {

            Cursor cursor = getContentResolver().query(DocumentContentProvider.CONTENT_URI, null,
                    Columns.PARENT_ID + "=? OR " + Columns.ID + "=?",
                    new String[] { String.valueOf(documentId), String.valueOf(documentId) }, "created ASC");
            cursor.moveToFirst();

            int index = cursor.getColumnIndex(Columns.TITLE);
            final String fileName = documentId + ".pdf";
            File outPdf = new File(dir, fileName);
            File outText = new File(dir, documentId + ".txt");

            mCurrentDocumentName = fileName;
            mCurrentPageCount = cursor.getCount();
            String[] images = new String[cursor.getCount()];
            String[] hocr = new String[cursor.getCount()];
            cursor.moveToPosition(-1);
            boolean overlayImage = true;
            while (cursor.moveToNext()) {
                int hocrIndex = cursor.getColumnIndex(Columns.HOCR_TEXT);
                index = cursor.getColumnIndex(Columns.PHOTO_PATH);
                final String photoPath = cursor.getString(index);
                Uri imageUri = null;
                if (photoPath != null) {
                    imageUri = Uri.parse(photoPath);
                } else {
                    overlayImage = false;
                }
                images[cursor.getPosition()] = Util.getPathForUri(NewDocumentActivity.this, imageUri);
                index = cursor.getColumnIndex(Columns.OCR_TEXT);
                final String text = cursor.getString(index);
                if (text != null && text.length() > 0) {
                    hocr[cursor.getPosition()] = cursor.getString(hocrIndex);
                    FileWriter writer;
                    try {
                        writer = new FileWriter(outText);
                        final String s = Html.fromHtml(text).toString();
                        writer.write(s);
                        writer.close();
                    } catch (IOException ioException) {
                        if (outText.exists()) {
                            outText.delete();
                        }
                        outText = null;
                    }

                    mOCRText.append(text);
                } else {
                    hocr[cursor.getPosition()] = "";
                }
            }
            cursor.close();
            Hocr2Pdf pdf = new Hocr2Pdf(this);
            pdf.hocr2pdf(images, hocr, outPdf.getPath(), true, overlayImage);
            return new Pair<>(outPdf, outText);
        }

        @Override
        protected Pair<ArrayList<Uri>, ArrayList<Uri>> doInBackground(Void... params) {
            File dir = Util.getPDFDir();
            if (!dir.exists()) {
                if (!dir.mkdir()) {
                    return null;
                }
            }

            ArrayList<Uri> pdfFiles = new ArrayList<Uri>();
            ArrayList<Uri> txtFiles = new ArrayList<Uri>();
            mCurrentDocumentIndex = 0;
            for (long id : mIds) {
                final Pair<File, File> pair = createPDF(dir, id);
                final File pdf = pair.first;
                final File text = pair.second;
                if (pdf != null) {
                    pdfFiles.add(Uri.fromFile(pdf));
                }
                if (text != null) {
                    txtFiles.add(Uri.fromFile(text));
                }
                mCurrentDocumentIndex++;
            }
            return Pair.create(pdfFiles, txtFiles);
        }
    }

    protected class DeleteDocumentTask extends AsyncTask<Void, Void, Integer> {
        Set<Integer> mIds = new HashSet<Integer>();
        private final static int RESULT_REMOTE_EXCEPTION = -1;
        final boolean mFinishActivity;

        public DeleteDocumentTask(Set<Integer> parentDocumentIds, final boolean finishActivityAfterExecution) {
            mIds.addAll(parentDocumentIds);
            mFinishActivity = finishActivityAfterExecution;
        }

        @Override
        protected void onPreExecute() {
            Bundle args = new Bundle(2);
            args.putInt(DIALOG_ARG_MAX, mIds.size());
            String message = getText(R.string.delete_dialog_message).toString();
            args.putString(DIALOG_ARG_MESSAGE, message);
            showDialog(DELETE_PROGRESS_DIALOG_ID, args);
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Integer result) {
            if (result == RESULT_REMOTE_EXCEPTION) {
                Toast.makeText(getApplicationContext(), getText(R.string.delete_error), Toast.LENGTH_LONG).show();
            }
            dismissDialog(DELETE_PROGRESS_DIALOG_ID);
            super.onPostExecute(result);
            if (mFinishActivity) {
                finish();
            }
        }

        private int deleteDocument(Cursor c, ContentProviderClient client) throws RemoteException {
            int index = c.getColumnIndex(Columns.ID);
            int currentId = c.getInt(index);
            Uri currentDocumentUri = Uri.withAppendedPath(DocumentContentProvider.CONTENT_URI,
                    String.valueOf(currentId));
            index = c.getColumnIndex(Columns.PHOTO_PATH);
            String imagePath = c.getString(index);
            if (imagePath != null) {
                new File(imagePath).delete();
            }
            return client.delete(currentDocumentUri, null, null);
        }

        @Override
        protected Integer doInBackground(Void... params) {

            ContentProviderClient client = getContentResolver()
                    .acquireContentProviderClient(DocumentContentProvider.CONTENT_URI);

            int count = 0;
            int progress = 0;
            for (Integer id : mIds) {
                try {
                    Cursor c = client.query(DocumentContentProvider.CONTENT_URI,
                            new String[] { Columns.ID, Columns.PHOTO_PATH },
                            Columns.PARENT_ID + "=? OR " + Columns.ID + "=?",
                            new String[] { String.valueOf(id), String.valueOf(id) }, Columns.PARENT_ID + " ASC");

                    while (c.moveToNext()) {
                        count += deleteDocument(c, client);
                    }

                } catch (RemoteException exc) {
                    return RESULT_REMOTE_EXCEPTION;
                }
                deleteProgressDialog.setProgress(++progress);
            }
            return count;
        }
    }

    public static class SaveDocumentTask extends AsyncTask<Void, Integer, Integer> {

        private final Context mContext;
        private ContentValues values = new ContentValues();
        private ArrayList<Uri> mDocumentUri = new ArrayList<>();
        private String mTitle;
        private ArrayList<Spanned> mOcrText = new ArrayList<>();
        private Toast mSaveToast;

        public SaveDocumentTask(Context context, List<Uri> documentUri, List<Spanned> ocrText) {
            mContext = context;
            this.mDocumentUri.addAll(documentUri);
            this.mTitle = null;
            this.mOcrText.addAll(ocrText);
        }

        public SaveDocumentTask(Context context, Uri documentUri, String title) {
            mContext = context;
            this.mDocumentUri.add(documentUri);
            this.mTitle = title;
        }

        @Override
        protected void onPreExecute() {
            mSaveToast = Toast.makeText(mContext, mContext.getText(R.string.saving_document), Toast.LENGTH_LONG);
        }

        @Override
        protected void onPostExecute(Integer result) {
            if (result != null && result > 0) {
                mSaveToast.setText(R.string.save_success);
            } else {
                mSaveToast.setText(R.string.save_fail);
            }
        }

        @Override
        protected Integer doInBackground(Void... params) {
            mSaveToast.show();
            int result = 0;
            for (int i = 0; i < mDocumentUri.size(); i++) {
                values.clear();
                Uri uri = mDocumentUri.get(i);
                if (mOcrText != null && i < mOcrText.size()) {
                    final String text = Html.toHtml(mOcrText.get(i));
                    values.put(Columns.OCR_TEXT, text);

                }
                if (mTitle != null) {
                    values.put(Columns.TITLE, mTitle);
                }
                publishProgress(i);
                result += mContext.getContentResolver().update(uri, values, null, null);
            }
            return result;
        }
    }

}