org.liberty.android.fantastischmemo.ui.CardEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.liberty.android.fantastischmemo.ui.CardEditor.java

Source

/*
Copyright (C) 2012 Haowen Ning
    
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
See the GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    
*/
package org.liberty.android.fantastischmemo.ui;

import java.io.File;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.liberty.android.fantastischmemo.AMActivity;
import org.liberty.android.fantastischmemo.AMEnv;
import org.liberty.android.fantastischmemo.AMPrefKeys;
import org.liberty.android.fantastischmemo.AnyMemoDBOpenHelper;
import org.liberty.android.fantastischmemo.AnyMemoDBOpenHelperManager;
import org.liberty.android.fantastischmemo.R;
import org.liberty.android.fantastischmemo.dao.CardDao;
import org.liberty.android.fantastischmemo.dao.CategoryDao;
import org.liberty.android.fantastischmemo.dao.LearningDataDao;
import org.liberty.android.fantastischmemo.domain.Card;
import org.liberty.android.fantastischmemo.domain.Category;
import org.liberty.android.fantastischmemo.domain.LearningData;
import org.liberty.android.fantastischmemo.ui.AudioRecorderFragment.AudioRecorderResultListener;
import org.liberty.android.fantastischmemo.ui.CategoryEditorFragment.CategoryEditorResultListener;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;

public class CardEditor extends AMActivity {
    private final int ACTIVITY_IMAGE_FILE = 1;
    private final int ACTIVITY_AUDIO_FILE = 2;
    Card currentCard = null;
    Card prevCard = null;
    private Integer prevOrdinal = null;
    private Integer currentCardId;
    private EditText questionEdit;
    private EditText answerEdit;
    private Button categoryButton;
    private EditText noteEdit;
    private RadioGroup addRadio;
    private boolean addBack = true;
    private boolean isEditNew = false;
    private String dbName = null;
    String dbPath = null;
    CardDao cardDao;
    CategoryDao categoryDao;
    LearningDataDao learningDataDao;
    private InitTask initTask;
    private AnyMemoDBOpenHelper helper;

    private String originalQuestion;
    private String originalAnswer;
    private String originalNote;

    public static String EXTRA_DBPATH = "dbpath";
    public static String EXTRA_CARD_ID = "id";
    public static String EXTRA_RESULT_CARD_ID = "result_card_id";
    public static String EXTRA_IS_EDIT_NEW = "is_edit_new";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.card_editor_layout);
        initTask = new InitTask();
        initTask.execute((Void) null);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        AnyMemoDBOpenHelperManager.releaseHelper(helper);
    }

    @Override
    public void restartActivity() {
        assert currentCard != null : "Null card is used when restarting activity";
        assert dbPath != null : "Use null dbPath to restartAcitivity";
        Intent myIntent = new Intent(this, CardEditor.class);
        myIntent.putExtra(EXTRA_CARD_ID, currentCard.getId());
        myIntent.putExtra(EXTRA_DBPATH, dbPath);
        finish();
        startActivity(myIntent);
    }

    @Override
    public void onBackPressed() {
        String qText = questionEdit.getText().toString();
        String aText = answerEdit.getText().toString();
        String nText = noteEdit.getText().toString();
        if (!isEditNew && (!qText.equals(originalQuestion) || !aText.equals(originalAnswer)
                || !nText.equals(originalNote))) {
            new AlertDialog.Builder(this).setTitle(R.string.warning_text)
                    .setMessage(R.string.edit_dialog_unsave_warning)
                    .setPositiveButton(R.string.yes_text, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface d, int which) {
                            SaveCardTask task = new SaveCardTask();
                            task.execute((Void) null);
                        }
                    }).setNeutralButton(R.string.no_text, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface d, int which) {
                            Intent resultIntent = new Intent();
                            setResult(Activity.RESULT_CANCELED, resultIntent);
                            finish();
                        }
                    }).setNegativeButton(R.string.cancel_text, null).create().show();

        } else {
            Intent resultIntent = new Intent();
            setResult(Activity.RESULT_CANCELED, resultIntent);
            finish();

        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.card_editor_menu, menu);
        return true;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        View focusView = getCurrentFocus();
        switch (item.getItemId()) {

        case R.id.save:
            SaveCardTask task = new SaveCardTask();
            task.execute((Void) null);
            return true;

        case R.id.editor_menu_br:
            if (focusView == questionEdit || focusView == answerEdit || focusView == noteEdit) {
                addTextToView((EditText) focusView, "<br />");
            }
            return true;

        case R.id.editor_menu_image:
            if (focusView == questionEdit || focusView == answerEdit || focusView == noteEdit) {
                Intent myIntent = new Intent(this, FileBrowserActivity.class);
                myIntent.putExtra(FileBrowserActivity.EXTRA_FILE_EXTENSIONS, ".png,.jpg,.tif,.bmp");
                startActivityForResult(myIntent, ACTIVITY_IMAGE_FILE);
            }
            return true;

        case R.id.add_existing_audio:
            if (isViewEligibleToEditAudio()) {
                addExistingAudio();
            }
            return true;

        case R.id.add_new_audio:
            if (isViewEligibleToEditAudio()) {
                addNewAudio();
            }
            return true;

        case R.id.remove_audio:
            if (isViewEligibleToEditAudio()) {
                removeAudio();
            }
            return true;

        }
        return false;
    }

    private boolean isViewEligibleToEditAudio() {
        View focusView = getCurrentFocus();
        if (focusView == questionEdit || focusView == answerEdit) {
            return true;
        } else {
            return false;
        }
    }

    private void showConfirmDialog(String msg, DialogInterface.OnClickListener positiveClickListener) {
        new AlertDialog.Builder(this).setMessage(msg)
                .setPositiveButton(getString(R.string.yes_text), positiveClickListener)
                .setNegativeButton(getString(R.string.no_text), null).create().show();

    }

    private boolean audioPreviouslyExists() {
        View focusView = getCurrentFocus();
        String curContent = ((EditText) focusView).getText().toString();
        return curContent.contains("src=");
    }

    private void addExistingAudio() {
        if (audioPreviouslyExists()) {
            //if there is audio previously defined,show alert
            DialogInterface.OnClickListener positiveClickListener = new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    startAudioBrowser();
                }
            };
            showConfirmDialog(getString(R.string.override_audio_warning_text), positiveClickListener);
        } else {
            startAudioBrowser();
        }
    }

    private void addNewAudio() {
        if (audioPreviouslyExists()) {
            //if there is audio previously defined,show alert
            DialogInterface.OnClickListener positiveClickListener = new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    startAudioRecorder();
                }
            };
            showConfirmDialog(getString(R.string.override_audio_warning_text), positiveClickListener);
        } else {
            startAudioRecorder();
        }
    }

    private void removeAudio() {
        View focusView = getCurrentFocus();
        if (focusView == questionEdit) {
            currentCard.setQuestion(currentCard.getQuestion().replaceAll("<audio src=.*/>", ""));
            ((EditText) focusView).setText(currentCard.getQuestion());
        } else if (focusView == answerEdit) {
            currentCard.setAnswer(currentCard.getAnswer().replaceAll("<audio src=.*/>", ""));
            ((EditText) focusView).setText(currentCard.getAnswer());
        } else {
            return;
        }
    }

    private void startAudioBrowser() {
        removeAudio();
        Intent myIntent = new Intent(this, FileBrowserActivity.class);
        myIntent.putExtra(FileBrowserActivity.EXTRA_FILE_EXTENSIONS, ".3gp,.ogg,.mp3,.wav,.amr");
        startActivityForResult(myIntent, ACTIVITY_AUDIO_FILE);
    }

    private void startAudioRecorder() {
        removeAudio();
        View focusView = getCurrentFocus();
        String audioFilename = AMEnv.DEFAULT_AUDIO_PATH + dbName;
        new File(audioFilename).mkdirs();
        AudioRecorderFragment recorder = new AudioRecorderFragment();
        if (focusView == questionEdit) {
            audioFilename += "/" + currentCardId + "_q.3gp";
        } else if (focusView == answerEdit) {
            audioFilename += "/" + currentCardId + "_a.3gp";
        } else {
            return;
        }
        Bundle b = new Bundle();
        b.putString(AudioRecorderFragment.EXTRA_AUDIO_FILENAME, audioFilename);

        recorder.setAudioRecorderResultListener(new AudioRecorderResultListener() {
            public void onReceiveAudio() {
                View focusView = getCurrentFocus();
                if (focusView == questionEdit) {
                    addTextToView((EditText) focusView, "<audio src=\"" + currentCardId + "_q.3gp\" />");
                } else if (focusView == answerEdit) {
                    addTextToView((EditText) focusView, "<audio src=\"" + currentCardId + "_a.3gp\" />");
                }
            }
        });
        recorder.setArguments(b);
        recorder.show(getSupportFragmentManager(), "AudioRecorderDialog");
    }

    private void addTextToView(EditText v, String text) {
        String origText = v.getText().toString();
        /*
         * keep track of the cursor location and restore it
         * after pasting because the default location is the
         * begining of the EditText
         */
        int cursorPos = v.getSelectionStart();
        try {
            String newText = origText.substring(0, cursorPos) + text
                    + origText.substring(cursorPos, origText.length());

            v.setText(newText);
            v.setSelection(cursorPos + text.length());

        } catch (Exception e) {
            Log.e(TAG, "cursor position is wrong", e);
        }
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        String name, path;
        switch (requestCode) {
        case ACTIVITY_IMAGE_FILE:
            if (resultCode == Activity.RESULT_OK) {
                View focusView = getCurrentFocus();
                if (focusView == questionEdit || focusView == answerEdit || focusView == noteEdit) {
                    path = data.getStringExtra(FileBrowserActivity.EXTRA_RESULT_PATH);
                    name = FilenameUtils.getName(path);
                    addTextToView((EditText) focusView, "<img src=\"" + name + "\" />");
                    /* Copy the image to correct location */
                    String imageRoot = AMEnv.DEFAULT_IMAGE_PATH;
                    String imagePath = imageRoot + dbName + "/";
                    new File(imageRoot).mkdir();
                    new File(imagePath).mkdir();
                    try {
                        String target = imagePath + name;
                        if (!(new File(target)).exists()) {
                            FileUtils.copyFile(new File(path), new File(target));
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "Error copying image", e);
                    }
                }
            }
            break;
        case ACTIVITY_AUDIO_FILE:
            if (resultCode == Activity.RESULT_OK) {
                View focusView = getCurrentFocus();
                if (focusView == questionEdit || focusView == answerEdit || focusView == noteEdit) {
                    path = data.getStringExtra(FileBrowserActivity.EXTRA_RESULT_PATH);
                    name = FilenameUtils.getName(path);
                    addTextToView((EditText) focusView, "<audio src=\"" + name + "\" />");
                    /* Copy the image to correct location */
                    String audioRoot = AMEnv.DEFAULT_AUDIO_PATH;
                    String audioPath = audioRoot + dbName + "/";
                    new File(audioRoot).mkdir();
                    new File(audioPath).mkdir();
                    try {
                        String target = audioPath + name;
                        if (!(new File(target)).exists()) {
                            FileUtils.copyFile(new File(path), new File(audioPath + name));
                        }
                    } catch (Exception e) {
                        Log.e(TAG, "Error copying audio", e);
                    }
                }
            }
            break;
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }

    private void setInitRadioButton() {
        if (!isEditNew) {
            addRadio.setVisibility(View.GONE);
            addBack = false;
        } else {
            /*
             * The radio button is only valid when the user is creating
             * new items. If the user is editng, it has no effect at all
             */
            addRadio.setVisibility(View.VISIBLE);
            final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
            // Only for new card we need to add back.
            // For existing cards, we just edit the current card.
            addBack = settings.getBoolean(AMPrefKeys.ADD_BACK_KEY, true);
            if (addBack) {
                addRadio.check(R.id.add_back_radio);
            } else {
                addRadio.check(R.id.add_here_radio);
            }
            RadioGroup.OnCheckedChangeListener changeListener = new RadioGroup.OnCheckedChangeListener() {
                public void onCheckedChanged(RadioGroup group, int checkedId) {
                    SharedPreferences.Editor editor = settings.edit();
                    if (checkedId == R.id.add_here_radio) {
                        addBack = false;
                        editor.putBoolean(AMPrefKeys.ADD_BACK_KEY, false);
                        editor.commit();
                    } else {
                        addBack = true;
                        editor.putBoolean(AMPrefKeys.ADD_BACK_KEY, true);
                        editor.commit();
                    }
                }
            };
            addRadio.setOnCheckedChangeListener(changeListener);
        }
    }

    private void updateViews() {
        updateCategoryView();

        /* Prefill the note if it is empty */
        if (isEditNew) {
            /* Use this one or the one below ?*/
            noteEdit.setText(currentCard.getNote());
        }
        if (!isEditNew) {
            originalQuestion = currentCard.getQuestion();
            originalAnswer = currentCard.getAnswer();
            originalNote = currentCard.getNote();
            questionEdit.setText(originalQuestion);
            answerEdit.setText(originalAnswer);
            noteEdit.setText(originalNote);
        }
    }

    private void updateCategoryView() {
        /* Retain the last category when editing new */
        String categoryName = currentCard.getCategory().getName();
        if (categoryName.equals("")) {
            categoryButton.setText(R.string.uncategorized_text);
        } else {
            categoryButton.setText(categoryName);
        }
    }

    private class InitTask extends AsyncTask<Void, Void, Void> {
        private ProgressDialog progressDialog;

        @Override
        public void onPreExecute() {
            setTitle(R.string.memo_edit_dialog_title);
            Bundle extras = getIntent().getExtras();
            if (extras != null) {
                currentCardId = extras.getInt(EXTRA_CARD_ID);
                dbPath = extras.getString(EXTRA_DBPATH);
                dbName = FilenameUtils.getName(dbPath);
                isEditNew = extras.getBoolean(EXTRA_IS_EDIT_NEW);
            }

            progressDialog = new ProgressDialog(CardEditor.this);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            progressDialog.setTitle(getString(R.string.loading_please_wait));
            progressDialog.setMessage(getString(R.string.loading_database));
            progressDialog.setCancelable(false);
            progressDialog.show();
            assert dbPath != null : "dbPath shouldn't be null";
        }

        @Override
        public Void doInBackground(Void... params) {
            helper = AnyMemoDBOpenHelperManager.getHelper(CardEditor.this, dbPath);

            cardDao = helper.getCardDao();
            categoryDao = helper.getCategoryDao();
            learningDataDao = helper.getLearningDataDao();

            Card prevCard = cardDao.queryForId(currentCardId);

            if (prevCard != null) {
                prevOrdinal = prevCard.getOrdinal();
            }
            if (isEditNew) {
                currentCard = new Card();
                // Search for "Uncategorized".
                Category c = categoryDao.queryForId(1);
                currentCard.setCategory(c);
                // Save the ordinal to be used when saving.
                LearningData ld = new LearningData();
                learningDataDao.create(ld);
                currentCard.setLearningData(ld);
            } else {
                currentCard = prevCard;
            }
            assert currentCard != null : "Try to edit null card!";
            categoryDao.refresh(currentCard.getCategory());

            return null;
        }

        @Override
        public void onPostExecute(Void result) {
            // It means empty set
            questionEdit = (EditText) findViewById(R.id.edit_dialog_question_entry);
            answerEdit = (EditText) findViewById(R.id.edit_dialog_answer_entry);
            categoryButton = (Button) findViewById(R.id.edit_dialog_category_button);
            noteEdit = (EditText) findViewById(R.id.edit_dialog_note_entry);
            addRadio = (RadioGroup) findViewById(R.id.add_radio);

            categoryButton.setOnClickListener(categoryButtonClickListener);

            updateViews();

            /* Should be called after the private fields are inited */
            setInitRadioButton();
            progressDialog.dismiss();
        }
    }

    private View.OnClickListener categoryButtonClickListener = new View.OnClickListener() {
        public void onClick(View v) {
            CategoryEditorFragment df = new CategoryEditorFragment();
            df.setResultListener(categoryResultListener);
            Bundle b = new Bundle();
            b.putString(CategoryEditorFragment.EXTRA_DBPATH, dbPath);
            b.putInt(CategoryEditorFragment.EXTRA_CATEGORY_ID, currentCard.getCategory().getId());
            df.setArguments(b);
            df.show(getSupportFragmentManager(), "CategoryEditDialog");
        }
    };

    private class SaveCardTask extends AsyncTask<Void, Void, Void> {
        private ProgressDialog progressDialog;

        @Override
        public void onPreExecute() {
            progressDialog = new ProgressDialog(CardEditor.this);
            progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            progressDialog.setTitle(getString(R.string.loading_please_wait));
            progressDialog.setMessage(getString(R.string.loading_database));
            progressDialog.setCancelable(false);
            progressDialog.show();

            String qText = questionEdit.getText().toString();
            String aText = answerEdit.getText().toString();
            String nText = noteEdit.getText().toString();
            currentCard.setQuestion(qText);
            currentCard.setAnswer(aText);
            currentCard.setNote(nText);

            assert currentCard != null : "Current card shouldn't be null";
        }

        @Override
        public Void doInBackground(Void... params) {
            if (prevOrdinal != null && !addBack) {
                currentCard.setOrdinal(prevOrdinal);
            } else {
                Card lastCard = cardDao.queryLastOrdinal();
                // last card = null means this is the first card to add
                // We should set ordinal to 1.
                if (lastCard == null) {
                    currentCard.setOrdinal(1);
                } else {
                    int lastOrd = lastCard.getOrdinal();
                    currentCard.setOrdinal(lastOrd + 1);
                }
            }
            if (isEditNew) {
                cardDao.create(currentCard);
            } else {
                cardDao.update(currentCard);
            }

            return null;
        }

        @Override
        public void onPostExecute(Void result) {
            progressDialog.dismiss();
            Intent resultIntent = new Intent();
            resultIntent.putExtra(EXTRA_RESULT_CARD_ID, currentCard.getId());
            setResult(Activity.RESULT_OK, resultIntent);
            finish();
        }
    }

    // When a category is selected in category fragment.
    private CategoryEditorResultListener categoryResultListener = new CategoryEditorResultListener() {
        public void onReceiveCategory(Category c) {
            currentCard.setCategory(c);
            updateCategoryView();
        }
    };
}