Java tutorial
/* 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.util.HashMap; import java.util.regex.Pattern; import javax.inject.Inject; import org.liberty.android.fantastischmemo.AMPrefKeys; import org.liberty.android.fantastischmemo.R; 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.domain.Setting; import org.liberty.android.fantastischmemo.ui.CategoryEditorFragment.CategoryEditorResultListener; import org.liberty.android.fantastischmemo.utils.AMGUIUtility; import org.liberty.android.fantastischmemo.utils.AMPrefUtil; import org.liberty.android.fantastischmemo.utils.ShareUtil; import roboguice.util.Ln; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.SearchView; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.google.common.base.Preconditions; public class PreviewEditActivity extends QACardActivity { private final int ACTIVITY_EDIT = 11; private final int ACTIVITY_SETTINGS = 15; private final int ACTIVITY_LIST = 16; private final int ACTIVITY_MERGE = 17; private final int ACTIVITY_DETAIL = 18; private final int ACTIVITY_CARD_PLAYER = 19; private final static String WEBSITE_HELP_EDIT = "http://anymemo.org/wiki/index.php?title=Editing_screen"; private long totalCardCount = 0; public static String EXTRA_DBPATH = "dbpath"; public static String EXTRA_CARD_ID = "id"; public static String EXTRA_CATEGORY = "category"; private static final String SEARCH_BY_ID_PATTERN = "#\\d+"; private Category currentCategory = null; private Integer savedCardId = null; private String dbPath = ""; private int activeCategoryId = -1; private Button newButton; private Button editButton; private Button prevButton; private Button nextButton; // Injected objects private ShareUtil shareUtil; private AMPrefUtil amPrefUtil; // The first card to read and display. private int startCardId = 1; @Inject public void setShareUtil(ShareUtil shareUtil) { this.shareUtil = shareUtil; } @Inject public void setAmPrefUtil(AMPrefUtil amPrefUtil) { this.amPrefUtil = amPrefUtil; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle extras = getIntent().getExtras(); assert extras != null : "Extras for PreviewEditActivity should have at least dbPath!"; dbPath = extras.getString(EXTRA_DBPATH); activeCategoryId = extras.getInt(EXTRA_CATEGORY, -1); startCardId = extras.getInt(EXTRA_CARD_ID, -1); if (savedInstanceState != null) { startCardId = savedInstanceState.getInt(EXTRA_CARD_ID, -1); } /* * Currently always set the result to OK * to assume there are always some changes. * This may be changed in the future to reflect the * real changes */ setResult(Activity.RESULT_OK); startInit(); } @Override protected void onSaveInstanceState(Bundle outState) { Card currentCard = getCurrentCard(); if (currentCard != null) { outState.putInt(EXTRA_CARD_ID, currentCard.getId()); } } @Override public int getContentView() { return R.layout.qa_card_layout_preview_edit; } @Override public void onPostInit() { super.onPostInit(); Card currentCard = null; // If category is set, it will override the card id. if (activeCategoryId != -1) { currentCategory = getDbOpenHelper().getCategoryDao().queryForId(activeCategoryId); currentCard = getDbOpenHelper().getCardDao().queryFirstOrdinal(currentCategory); } else if (startCardId != -1) { currentCard = getDbOpenHelper().getCardDao().queryForId(startCardId); } // If None of category and card is is set, first ordinal is queried // Note curretnCategory should be null. if (currentCard == null) { currentCard = getDbOpenHelper().getCardDao().queryFirstOrdinal(currentCategory); } totalCardCount = getDbOpenHelper().getCardDao().countOf(); setCurrentCard(currentCard); composeViews(); //currentCard = .getItem(currentId); setViewListeners(); if (getCurrentCard() != null) { updateCardFrontSide(); updateTitle(); } } // Save the card id in onPause @Override public void onPause() { super.onPause(); if (getCurrentCard() != null) { amPrefUtil.putSavedInt(AMPrefKeys.PREVIEW_EDIT_START_ID_PREFIX, dbPath, getCurrentCard().getId()); } } @Override public void onDestroy() { super.onDestroy(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_CANCELED) { return; } /* Refresh the activity according to activities */ switch (requestCode) { case ACTIVITY_EDIT: { Bundle extras = data.getExtras(); int cardId = extras.getInt(CardEditor.EXTRA_RESULT_CARD_ID, 1); Card card = getDbOpenHelper().getCardDao().queryForId(cardId); if (card != null) { setCurrentCard(card); } restartActivity(); break; } case ACTIVITY_CARD_PLAYER: { Bundle extras = data.getExtras(); int cardId = extras.getInt(CardPlayerActivity.EXTRA_RESULT_CARD_ID, 1); Card card = getDbOpenHelper().getCardDao().queryForId(cardId); if (card != null) { setCurrentCard(card); } restartActivity(); break; } case ACTIVITY_SETTINGS: { restartActivity(); break; } case ACTIVITY_LIST: { restartActivity(); break; } case ACTIVITY_MERGE: { restartActivity(); break; } case ACTIVITY_DETAIL: { restartActivity(); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.preview_edit_activity_menu, menu); MenuItem searchItem = menu.findItem(R.id.action_search); SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); searchView.setOnQueryTextListener(onQueryTextChangedListener); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menuspeakquestion: { if (getCurrentCard() != null) { return speakQuestion(); } return true; } case R.id.menuspeakanswer: { if (getCurrentCard() != null) { return speakAnswer(); } return true; } case R.id.editmenu_settings_id: { Intent myIntent = new Intent(this, SettingsScreen.class); myIntent.putExtra(SettingsScreen.EXTRA_DBPATH, dbPath); startActivityForResult(myIntent, ACTIVITY_SETTINGS); } return true; case R.id.editmenu_delete_id: { deleteCard(getCurrentCard()); return true; } case R.id.editmenu_detail_id: { if (getCurrentCard() != null) { Intent myIntent = new Intent(this, DetailScreen.class); myIntent.putExtra(DetailScreen.EXTRA_DBPATH, this.dbPath); myIntent.putExtra(DetailScreen.EXTRA_CARD_ID, getCurrentCard().getId()); startActivityForResult(myIntent, ACTIVITY_DETAIL); } return true; } case R.id.editmenu_list_id: { // List edit mode Intent myIntent = new Intent(this, SettingsScreen.class); myIntent.setClass(this, CardListActivity.class); myIntent.putExtra(CardListActivity.EXTRA_DBPATH, dbPath); if (getCurrentCard() != null) { myIntent.putExtra("openid", getCurrentCard().getId()); } startActivityForResult(myIntent, ACTIVITY_LIST); return true; } case R.id.menu_edit_categories: { if (getCurrentCard() != null) { showCategoriesDialog(); } return true; } case R.id.editmenu_help: { Intent myIntent = new Intent(); myIntent.setAction(Intent.ACTION_VIEW); myIntent.addCategory(Intent.CATEGORY_BROWSABLE); myIntent.setData(Uri.parse(WEBSITE_HELP_EDIT)); startActivity(myIntent); return true; } case R.id.menu_card_player: { if (getCurrentCard() != null) { Intent intent = new Intent(this, CardPlayerActivity.class); intent.putExtra(CardPlayerActivity.EXTRA_DBPATH, dbPath); if (getCurrentCard() != null) { intent.putExtra(CardPlayerActivity.EXTRA_START_CARD_ID, getCurrentCard().getId()); } startActivityForResult(intent, ACTIVITY_CARD_PLAYER); } return true; } case R.id.menu_context_copy: { if (getCurrentCard() != null) { Toast.makeText(this, R.string.copy_text, Toast.LENGTH_LONG).show(); savedCardId = getCurrentCard().getId(); } return true; } case R.id.menu_context_paste: { if (savedCardId != null && getCurrentCard() != null) { Card savedCard = getDbOpenHelper().getCardDao().queryForId(savedCardId); LearningData ld = new LearningData(); getDbOpenHelper().getLearningDataDao().create(ld); savedCard.setLearningData(ld); savedCard.setOrdinal(getCurrentCard().getOrdinal()); getDbOpenHelper().getCardDao().create(savedCard); restartActivity(); } return true; } case R.id.menu_context_swap_current: { getDbOpenHelper().getCardDao().swapQA(getCurrentCard()); restartActivity(); return true; } case R.id.menu_context_reset_current: { getDbOpenHelper().getLearningDataDao().resetLearningData(getCurrentCard().getLearningData()); return true; } case R.id.menu_context_wipe: { AMGUIUtility.doConfirmProgressTask(this, R.string.settings_wipe, R.string.settings_wipe_warning, R.string.loading_please_wait, R.string.loading_save, new AMGUIUtility.ProgressTask() { @Override public void doHeavyTask() { getDbOpenHelper().getLearningDataDao().resetAllLearningData(); } @Override public void doUITask() { /* Do nothing */} }); return true; } case R.id.menu_context_swap: { new AlertDialog.Builder(this).setTitle(R.string.warning_text).setIcon(R.drawable.alert_dialog_icon) .setMessage(R.string.settings_inverse_warning) .setPositiveButton(R.string.swap_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface arg0, int arg1) { AMGUIUtility.doProgressTask(PreviewEditActivity.this, R.string.loading_please_wait, R.string.loading_save, new AMGUIUtility.ProgressTask() { public void doHeavyTask() { getDbOpenHelper().getCardDao().swapAllQA(); } public void doUITask() { restartActivity(); } }); } }).setNeutralButton(R.string.swapdup_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface arg0, int arg1) { AMGUIUtility.doProgressTask(PreviewEditActivity.this, R.string.loading_please_wait, R.string.loading_save, new AMGUIUtility.ProgressTask() { public void doHeavyTask() { getDbOpenHelper().getCardDao().swapAllQADup(); } public void doUITask() { restartActivity(); } }); } }).setNegativeButton(R.string.cancel_text, null).create().show(); return true; } case R.id.menu_gestures: { showGesturesDialog(); return true; } case R.id.menu_context_remove_dup: { AMGUIUtility.doConfirmProgressTask(this, R.string.remove_dup_text, R.string.remove_dup_message, R.string.removing_dup_title, R.string.removing_dup_warning, new AMGUIUtility.ProgressTask() { @Override public void doHeavyTask() { getDbOpenHelper().getCardDao().removeDuplicates(); } @Override public void doUITask() { restartActivity(); } }); return true; } case R.id.menu_context_merge_db: { Intent myIntent = new Intent(this, DatabaseMerger.class); myIntent.putExtra(DatabaseMerger.EXTRA_SRC_PATH, dbPath); startActivityForResult(myIntent, ACTIVITY_MERGE); return true; } case R.id.menu_context_shuffle: { AMGUIUtility.doConfirmProgressTask(this, R.string.settings_shuffle, R.string.settings_shuffle_warning, R.string.loading_please_wait, R.string.loading_save, new AMGUIUtility.ProgressTask() { @Override public void doHeavyTask() { getDbOpenHelper().getCardDao().shuffleOrdinals(); } @Override public void doUITask() { restartActivity(); } }); return true; } case R.id.menu_share: { shareUtil.shareCard(getCurrentCard()); return true; } } return false; } @Override // Disable the copy to clipboard for Preview/Edit activity. protected void copyToClipboard() { Ln.v("Copy to clipboard is disabled for PreviewEditActivity"); } @Override protected void onGestureDetected(GestureName gestureName) { switch (gestureName) { case O_SHAPE: break; case S_SHAPE: break; case LEFT_SWIPE: gotoNext(); break; case RIGHT_SWIPE: gotoPrev(); break; default: break; } } @Override public void restartActivity() { Intent myIntent = new Intent(this, PreviewEditActivity.class); assert dbPath != null : "Use null dbPath to restartAcitivity"; myIntent.putExtra(EXTRA_DBPATH, dbPath); if (getCurrentCard() != null) { myIntent.putExtra(EXTRA_CARD_ID, getCurrentCard().getId()); } myIntent.putExtra(EXTRA_CATEGORY, activeCategoryId); finish(); startActivity(myIntent); } private void composeViews() { newButton = (Button) findViewById(R.id.new_button); editButton = (Button) findViewById(R.id.edit_button); prevButton = (Button) findViewById(R.id.prev_button); nextButton = (Button) findViewById(R.id.next_button); } private void setViewListeners() { ///* Set button listeners */ newButton.setOnClickListener(newButtonListener); editButton.setOnClickListener(editButtonListener); prevButton.setOnClickListener(prevButtonListener); nextButton.setOnClickListener(nextButtonListener); } private void updateTitle() { if (getCurrentCard() != null) { StringBuilder sb = new StringBuilder(); sb.append(getString(R.string.total_text) + ": " + totalCardCount + " "); sb.append(getString(R.string.id_text) + ": " + getCurrentCard().getId() + " "); sb.append(getString(R.string.ordinal_text_short) + ": " + getCurrentCard().getOrdinal() + " "); sb.append(getCurrentCard().getCategory().getName()); setSmallTitle(sb.toString()); } } private void gotoNext() { if (getCurrentCard() != null) { Card nextCard = getDbOpenHelper().getCardDao().queryNextCard(getCurrentCard(), currentCategory); assert nextCard != null : "Next card is null"; gotoCard(nextCard); } } protected void gotoCard(Card card) { Preconditions.checkNotNull(card); setCurrentCard(card); updateCardFrontSide(); updateTitle(); } private void deleteCard(final Card cardToDelete) { Preconditions.checkNotNull(cardToDelete); new AlertDialog.Builder(this).setTitle(getString(R.string.delete_text)) .setMessage(getString(R.string.delete_warning)) .setPositiveButton(getString(R.string.yes_text), new DialogInterface.OnClickListener() { public void onClick(DialogInterface arg0, int arg1) { DeleteCardTask task = new DeleteCardTask(); task.execute((Void) null); } }).setNegativeButton(getString(R.string.no_text), null).create().show(); } protected void gotoPrev() { if (getCurrentCard() != null) { Card prevCard = getDbOpenHelper().getCardDao().queryPrevCard(getCurrentCard(), currentCategory); assert prevCard != null : "Prev card is null"; gotoCard(prevCard); updateTitle(); } } /* * Show the front side of the current card * This method is called instead directly update the flashcard * so both single and double sided card will work. */ private void updateCardFrontSide() { if (getCurrentCard() != null) { if (getSetting().getCardStyle() == Setting.CardStyle.DOUBLE_SIDED) { /* Double sided card, show front */ displayCard(false); } else { /* Single sided, show both answer and questjion. */ displayCard(true); } } } private void showCategoriesDialog() { CategoryEditorFragment df = new CategoryEditorFragment(); df.setResultListener(categoryResultListener); Bundle b = new Bundle(); b.putString(CategoryEditorFragment.EXTRA_DBPATH, dbPath); if (currentCategory == null) { b.putInt(CategoryEditorFragment.EXTRA_CATEGORY_ID, getCurrentCard().getCategory().getId()); } else { // If we use the category filer, we can just use the currentCategory // This will handle the new card situation. b.putInt(CategoryEditorFragment.EXTRA_CATEGORY_ID, currentCategory.getId()); } df.setArguments(b); df.show(getSupportFragmentManager(), "CategoryEditDialog"); getSupportFragmentManager().findFragmentByTag("CategoryEditDialog"); } private void showGesturesDialog() { final HashMap<String, String> gestureNameDescriptionMap = new HashMap<String, String>(); gestureNameDescriptionMap.put(GestureName.LEFT_SWIPE.getName(), getString(R.string.add_screen_next)); gestureNameDescriptionMap.put(GestureName.RIGHT_SWIPE.getName(), getString(R.string.previous_text_short)); GestureSelectionDialogFragment df = new GestureSelectionDialogFragment(); Bundle args = new Bundle(); args.putSerializable(GestureSelectionDialogFragment.EXTRA_GESTURE_NAME_DESCRIPTION_MAP, gestureNameDescriptionMap); df.setArguments(args); df.show(getSupportFragmentManager(), "GestureSelectionDialog"); } private void searchCard(String text) { Preconditions.checkNotNull(text); SearchCardTask task = new SearchCardTask(); task.execute(text); } private View.OnClickListener prevButtonListener = new View.OnClickListener() { public void onClick(View v) { gotoPrev(); getCardTTSUtil().stopSpeak(); } }; private View.OnClickListener nextButtonListener = new View.OnClickListener() { public void onClick(View v) { gotoNext(); getCardTTSUtil().stopSpeak(); } }; private View.OnClickListener newButtonListener = new View.OnClickListener() { public void onClick(View v) { Intent myIntent = new Intent(PreviewEditActivity.this, CardEditor.class); myIntent.putExtra(CardEditor.EXTRA_DBPATH, dbPath); if (getCurrentCard() != null) { myIntent.putExtra(CardEditor.EXTRA_CARD_ID, getCurrentCard().getId()); } myIntent.putExtra(CardEditor.EXTRA_IS_EDIT_NEW, true); startActivityForResult(myIntent, ACTIVITY_EDIT); } }; private View.OnClickListener editButtonListener = new View.OnClickListener() { public void onClick(View v) { if (getCurrentCard() != null) { Intent myIntent = new Intent(PreviewEditActivity.this, CardEditor.class); myIntent.putExtra(CardEditor.EXTRA_DBPATH, dbPath); myIntent.putExtra(CardEditor.EXTRA_CARD_ID, getCurrentCard().getId()); myIntent.putExtra(CardEditor.EXTRA_IS_EDIT_NEW, false); startActivityForResult(myIntent, ACTIVITY_EDIT); } } }; // When a category is selected in category fragment. private CategoryEditorResultListener categoryResultListener = new CategoryEditorResultListener() { public void onReceiveCategory(Category c) { activeCategoryId = c.getId(); restartActivity(); } }; private class DeleteCardTask extends AsyncTask<Void, Void, Void> { private ProgressDialog progressDialog; @Override public void onPreExecute() { progressDialog = new ProgressDialog(PreviewEditActivity.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(); } @Override public Void doInBackground(Void... params) { Card delCard = getCurrentCard(); setCurrentCard(getDbOpenHelper().getCardDao().queryNextCard(getCurrentCard(), currentCategory)); getDbOpenHelper().getCardDao().delete(delCard); return null; } @Override public void onPostExecute(Void result) { restartActivity(); } } /* * params is the queyr text * Search Method should be in SearchMethod enum. */ private class SearchCardTask extends AsyncTask<String, Void, Card> { private ProgressDialog progressDialog; @Override public void onPreExecute() { progressDialog = new ProgressDialog(PreviewEditActivity.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(); } @Override public Card doInBackground(String... params) { String text = params[0]; assert text != null : "Pass null criteria to SearchCardTask"; Card foundCard = null; // Search #123 for id 123 if (Pattern.matches(SEARCH_BY_ID_PATTERN, text)) { int id = Integer.valueOf(text.substring(1)); foundCard = getDbOpenHelper().getCardDao().queryForId(id); } // Search normal cards if (foundCard == null) { // Search normal text if (!text.contains("*")) { text = "*" + text + "*"; } // Convert to SQL wildcard text = text.replace("*", "%"); text = text.replace("?", "_"); // First try to search from the current place foundCard = getDbOpenHelper().getCardDao().searchNextCard(text, getCurrentCard().getOrdinal()); } // If not found, search from the first card if (foundCard == null) { foundCard = getDbOpenHelper().getCardDao().searchNextCard(text, 0); } return foundCard; } @Override public void onPostExecute(Card result) { progressDialog.dismiss(); if (result == null) { return; } setCurrentCard(result); updateCardFrontSide(); updateTitle(); } } // Invoked when the search action bar is used to search cards SearchView.OnQueryTextListener onQueryTextChangedListener = new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String text) { return true; } @Override public boolean onQueryTextSubmit(String text) { searchCard(text); return true; } }; }