/*
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 android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.google.common.base.Strings;
import org.liberty.android.fantastischmemo.R;
import org.liberty.android.fantastischmemo.entity.Card;
import org.liberty.android.fantastischmemo.entity.Category;
import org.liberty.android.fantastischmemo.entity.Option;
import org.liberty.android.fantastischmemo.entity.Setting;
import org.liberty.android.fantastischmemo.modules.AppComponents;
import org.liberty.android.fantastischmemo.queue.QueueManager;
import org.liberty.android.fantastischmemo.queue.QuizQueueManager;
import org.liberty.android.fantastischmemo.scheduler.Scheduler;
import org.liberty.android.fantastischmemo.ui.loader.DBLoader;
import org.liberty.android.fantastischmemo.utils.DictionaryUtil;
import javax.inject.Inject;
public class QuizActivity extends QACardActivity {
public static String EXTRA_CATEGORY_ID = "category_id";
public static String EXTRA_START_CARD_ORD = "start_card_ord";
public static String EXTRA_QUIZ_SIZE = "quiz_size";
public static String EXTRA_SHUFFLE_CARDS = "shuffle_cards";
public static String EXTRA_START_CARD_ID = "start_card_id";
/* UI elements */
private GradeButtonsFragment gradeButtonsFragment;
/* Settings */
private Setting setting;
private Option option;
/* Utils */
@Inject DictionaryUtil dictionaryUtil;
private QuizQueueManager queueManager;
private int startCardId = -1;
private int categoryId = -1;
private int startCardOrd = -1;
private int quizSize = -1;
private boolean isNewCardsCompleted = false;
private boolean shuffleCards = false;
private int totalQuizSize = -1;
@Override
public int getContentView() {
return R.layout.qa_card_layout_study;
}
@Override
public void onPostInit() {
super.onPostInit();
setting = getSetting();
option = getOption();
createQueue();
// Keep track the initial total quiz size.
totalQuizSize = queueManager.getNewQueueSize();
/* Run the learnQueue init in a separate thread */
if (startCardId != -1) {
setCurrentCard(queueManager.dequeuePosition(startCardId));
} else {
setCurrentCard(queueManager.dequeue());
}
if (getCurrentCard() == null) {
showNoItemDialog();
return;
}
setupGradeButtons();
displayCard(false);
setSmallTitle(getActivityTitleString());
setTitle(getDbName());
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
categoryId = extras.getInt(EXTRA_CATEGORY_ID, -1);
startCardOrd = extras.getInt(EXTRA_START_CARD_ORD, -1);
quizSize = extras.getInt(EXTRA_QUIZ_SIZE, -1);
shuffleCards = extras.getBoolean(EXTRA_SHUFFLE_CARDS, false);
if (savedInstanceState != null) {
startCardId = savedInstanceState.getInt(EXTRA_START_CARD_ID, -1);
}
getMultipleLoaderManager().registerLoaderCallbacks(3, new QuizQueueManagerLoaderCallbacks(), false);
startInit();
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
Card currentCard = getCurrentCard();
if (currentCard != null) {
outState.putInt(EXTRA_START_CARD_ID, currentCard.getId());
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.quiz_activity_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_lookup:
{
dictionaryUtil.showLookupListDialog("" + getCurrentCard().getQuestion() + " " + getCurrentCard().getAnswer());
break;
}
case R.id.menu_speak_question:
{
speakQuestion();
break;
}
case R.id.menu_speak_answer:
{
speakAnswer();
break;
}
case R.id.menu_paint:
{
Intent myIntent = new Intent(this, StudyActivity.class);
startActivity(myIntent);
}
}
return false;
}
@Override
protected boolean onClickQuestionText() {
if ((option.getSpeakingType() == Option.SpeakingType.AUTOTAP
|| option.getSpeakingType() == Option.SpeakingType.TAP)) {
speakQuestion();
} else {
onClickQuestionView();
}
return true;
}
@Override
protected boolean onClickAnswerText() {
if (!isAnswerShown()) {
onClickAnswerView();
} else if ((option.getSpeakingType() == Option.SpeakingType.AUTOTAP
|| option.getSpeakingType() == Option.SpeakingType.TAP)) {
speakAnswer();
}
return true;
}
@Override
protected boolean onClickQuestionView() {
if (!isAnswerShown()) {
displayCard(true);
}
return true;
}
@Override
protected boolean onClickAnswerView() {
if (!isAnswerShown()) {
displayCard(true);
} else if (setting.getCardStyle() == Setting.CardStyle.DOUBLE_SIDED && isAnswerShown()) {
displayCard(false);
}
return true;
}
@Override
protected boolean onVolumeUpKeyPressed() {
if (isAnswerShown()) {
gradeButtonsFragment.gradeCurrentCard(0);
Toast.makeText(this, getString(R.string.grade_text) + " 0", Toast.LENGTH_SHORT).show();
} else {
displayCard(true);
}
return true;
}
@Override
protected boolean onVolumeDownKeyPressed() {
if (isAnswerShown()) {
gradeButtonsFragment.gradeCurrentCard(3);
Toast.makeText(this, getString(R.string.grade_text) + " 3", Toast.LENGTH_SHORT).show();
} else {
displayCard(true);
}
return true;
}
public static class QuizQueueManagerLoader extends
DBLoader<QueueManager> {
private int filterCategoryId = -1;
private int startCardOrd = -1;
private int quizSize = 0;
private boolean shuffleCards = false;
@Inject Scheduler scheduler;
public QuizQueueManagerLoader(AppComponents appComponents,
String dbPath, int filterCategoryId,
int startCardOrd, int quizSize,
boolean shuffleCards) {
super(appComponents.applicationContext(), dbPath);
appComponents.inject(this);
this.filterCategoryId = filterCategoryId;
this.startCardOrd = startCardOrd;
this.quizSize = quizSize;
this.shuffleCards = shuffleCards;
}
@Override
public QueueManager dbLoadInBackground() {
Category filterCategory = null;
if (filterCategoryId != -1) {
filterCategory = dbOpenHelper.getCategoryDao().queryForId(filterCategoryId);
}
QuizQueueManager.Builder builder = new QuizQueueManager.Builder()
.setDbOpenHelper(dbOpenHelper)
.setScheduler(scheduler)
.setStartCardOrd(startCardOrd)
.setFilterCategory(filterCategory)
.setShuffle(shuffleCards);
if (startCardOrd != -1) {
builder.setStartCardOrd(startCardOrd)
.setQuizSize(quizSize);
}
return builder.build();
}
}
private class QuizQueueManagerLoaderCallbacks implements
LoaderManager.LoaderCallbacks<QueueManager> {
@Override
public Loader<QueueManager> onCreateLoader(int arg0, Bundle arg1) {
Loader<QueueManager> loader = new QuizQueueManagerLoader(appComponents(), getDbPath(),
categoryId, startCardOrd, quizSize, shuffleCards);
loader.forceLoad();
return loader;
}
@Override
public void onLoadFinished(Loader<QueueManager> loader , QueueManager queueManager) {
QuizActivity.this.queueManager = (QuizQueueManager) queueManager;
getMultipleLoaderManager().checkAllLoadersCompleted();
}
@Override
public void onLoaderReset(Loader<QueueManager> arg0) {
// Do nothing now
}
}
private void createQueue() {
}
@Override
public void onPostDisplayCard() {
// When displaying new card, we should stop the TTS reading.
getCardTTSUtil().stopSpeak();
if (isAnswerShown()) {
gradeButtonsFragment.setVisibility(View.VISIBLE);
} else {
// The grade button should be gone for double sided cards.
if (setting.getCardStyle() == Setting.CardStyle.DOUBLE_SIDED) {
gradeButtonsFragment.setVisibility(View.GONE);
} else {
gradeButtonsFragment.setVisibility(View.INVISIBLE);
}
}
}
private void setupGradeButtons() {
gradeButtonsFragment = new GradeButtonsFragment();
Bundle args = new Bundle();
args.putString(GradeButtonsFragment.EXTRA_DBPATH, getDbPath());
gradeButtonsFragment.setArguments(args);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.buttons_root, gradeButtonsFragment);
ft.commit();
gradeButtonsFragment.setOnCardChangedListener(onCardChangedListener);
}
private CharSequence getActivityTitleString() {
StringBuilder sb = new StringBuilder();
sb.append(getString(R.string.quiz_text) + ": " + (totalQuizSize - queueManager.getNewQueueSize()) + "/" + totalQuizSize + " ");
sb.append(getString(R.string.review_short_text) + ": " + queueManager.getReviewQueueSize()+ " ");
sb.append(getString(R.string.id_text) + ": " + getCurrentCard().getId() + " ");
if (!Strings.isNullOrEmpty(getCurrentCard().getCategory().getName())) {
sb.append(getString(R.string.category_short_text) + ": " + getCurrentCard().getCategory().getName());
}
return sb.toString();
}
/* Called when all quiz is completed */
private void showCompleteAllDialog() {
new AlertDialog.Builder(this)
.setTitle(R.string.quiz_completed_text)
.setMessage(R.string.quiz_complete_summary)
.setPositiveButton(R.string.back_menu_text, flushAndQuitListener)
.setCancelable(false)
.show();
}
/* Called when all new cards are completed. */
private void showCompleteNewDialog(int correct) {
LayoutInflater layoutInflater
= (LayoutInflater)getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = layoutInflater.inflate(R.layout.quiz_summary_dialog, null);
TextView scoreView = (TextView) view.findViewById(R.id.score_text);
int score = correct * 100 / totalQuizSize;
scoreView.setText("" + score + "% (" + correct + "/" + totalQuizSize + ")");
new AlertDialog.Builder(this)
.setTitle(R.string.quiz_completed_text)
.setView(view)
.setPositiveButton(R.string.review_text, null)
.setNegativeButton(R.string.cancel_text, flushAndQuitListener)
.setCancelable(false)
.show();
}
// Current flush is not functional. So this method only quit and does not flush
// the queue.
private DialogInterface.OnClickListener flushAndQuitListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
finish();
}
};
private GradeButtonsFragment.OnCardChangedListener onCardChangedListener =
new GradeButtonsFragment.OnCardChangedListener() {
public void onCardChanged(Card prevCard, Card updatedCard) {
gradeButtonsFragment.setVisibility(View.INVISIBLE);
// Run the task to update the updatedCard in the queue
// and dequeue the next card
ChangeCardTask task = new ChangeCardTask(QuizActivity.this, updatedCard);
task.execute();
}
};
// Task to change the card after a card is graded
// It needs to update the old card and dequeue the new card
// and display it.
private class ChangeCardTask extends AsyncTask<Void, Void, Card> {
private int newQueueSizeBeforeDequeue;
private int reviewQueueSizeBeforeDequeue;
private Card updatedCard;
public ChangeCardTask(Context context, Card updatedCard) {
this.updatedCard = updatedCard;
}
@Override
protected Card doInBackground(Void... voids) {
queueManager.remove(getCurrentCard());
queueManager.update(updatedCard);
// Keep track of two values to dermine when to display dialog
// to promote the quiz completion
newQueueSizeBeforeDequeue = queueManager.getNewQueueSize();
reviewQueueSizeBeforeDequeue = queueManager.getReviewQueueSize();
Card nextCard = queueManager.dequeue();
return nextCard;
}
@Override
protected void onPostExecute(Card result) {
setProgressBarIndeterminateVisibility(false);
if(result == null){
showCompleteAllDialog();
return;
}
if (newQueueSizeBeforeDequeue <= 0 && !isNewCardsCompleted) {
showCompleteNewDialog(totalQuizSize - reviewQueueSizeBeforeDequeue);
isNewCardsCompleted = true;
}
// Stat data
setCurrentCard(result);
displayCard(false);
setSmallTitle(getActivityTitleString());
}
}
private void showNoItemDialog(){
new AlertDialog.Builder(this)
.setTitle(this.getString(R.string.memo_no_item_title))
.setMessage(this.getString(R.string.memo_no_item_message))
.setNeutralButton(getString(R.string.back_menu_text), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
/* Finish the current activity and go back to the last activity.
* It should be the open screen. */
finish();
}
})
.setOnCancelListener(new DialogInterface.OnCancelListener(){
public void onCancel(DialogInterface dialog){
finish();
}
})
.create()
.show();
}
}