Last active
August 29, 2015 14:23
-
-
Save marinoluck/836468a8f6010e9aca91 to your computer and use it in GitHub Desktop.
Quiz Presenter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Presenter created to show the quiz flow on a view. | |
* Manages the Quiz LifeCycle | |
* | |
* @author Lucas Marino mailto:[email protected]. | |
*/ | |
public class QuizPresenter implements Presenter { | |
// view where the model state is rendered | |
private final PlayQuizView view; | |
// Start Quiz interactor, provides me the Quiz to play | |
private final StartQuizInteractor startQuizInteractor; | |
// it is who manages the business rules | |
private final QuizController quizController; | |
private final GetQuizInteractor getQuizInteractor; | |
// CountDowns: The three cycles | |
private CountDownTimer startTimer; | |
private CountDownTimer playTimer; | |
private CountDownTimer endTimer; | |
// prompt we are playing now | |
private Prompt currentPrompt; | |
// Model | |
Quiz quiz; | |
// User I'm trying to beat | |
private User opponent; | |
//the current loop I'm running | |
private String currentLoop = QuizConstats.START_LOOP; | |
// state management | |
private boolean opponentAnswered; | |
private boolean userAnswered; | |
public long loopAccumulatedTime = 0; | |
GameState gameState; | |
public QuizPresenter(PlayQuizView view, StartQuizInteractor startQuizInteractor, | |
GetQuizInteractor getQuizInteractor) { | |
if (startQuizInteractor == null) { | |
throw new IllegalArgumentException("Constructor parameters cannot be null!!!"); | |
} | |
this.startQuizInteractor = startQuizInteractor; | |
this.getQuizInteractor = getQuizInteractor; | |
this.quizController = new QuizController(); | |
this.view = view; | |
} | |
/** | |
* Initializes the presenter by start retrieving the user list. | |
*/ | |
public void initialize() { | |
this.loadQuiz(); | |
} | |
/** | |
* Ask for the quiz | |
*/ | |
private void loadQuiz() { | |
this.hideViewRetry(); | |
this.showViewLoading(); | |
this.createQuizAndStartToPLay(); | |
} | |
private void onTimeOut(long prompt) { | |
if (!currentLoop.equals(QuizConstats.PLAY_LOOP) || prompt != currentPrompt.getPosition()) | |
return; | |
view.lockButtons(); | |
userAnswered = true; | |
// update the view | |
view.updateResponse("TIMEOUT", loopAccumulatedTime, | |
false, true); | |
view.renderBubble(); | |
if (opponentAnswered) { | |
view.updateOpponentResponse(true); | |
view.renderOpponentBubble(); | |
} | |
// If opponent already answered go to next loop, otherwise reschedule | |
if (opponentAnswered) { | |
playTimer.cancel(); | |
runEndLoop(); | |
} else { | |
//reschedule response | |
} | |
} | |
// User has seleted an option | |
public void onOptionSelected(int prompt, int option) { | |
view.lockButtons(); | |
userAnswered = true; | |
// add the response | |
quizController.addResponse(getMe().getId(), prompt, option, | |
loopAccumulatedTime); | |
// update the view | |
view.updateResponse(currentPrompt.getOptions().get(option - 1).getText(), loopAccumulatedTime, | |
quizController.isRight(prompt, option), true); | |
view.renderBubble(); | |
if (opponentAnswered) { | |
view.updateOpponentResponse(true); | |
view.renderOpponentBubble(); | |
} | |
// If opponent already answered go to next loop, otherwise reschedule | |
if (opponentAnswered) { | |
playTimer.cancel(); | |
runEndLoop(); | |
} else { | |
//reschedule response | |
} | |
} | |
// opponent has selected an option | |
public void onOpponentSelected(int prompt, int option) { | |
opponentAnswered = true; | |
view.updateOpponentResponse(currentPrompt.getOptions().get(option - 1).getText(), loopAccumulatedTime, | |
quizController.isRight(prompt, option), userAnswered); | |
view.renderOpponentBubble(); | |
// If user already answered go to next loop, otherwise wait | |
if (userAnswered) { | |
Log.d("QUIZ", "onOpponentSelected, cancelling playTimer "); | |
playTimer.cancel(); | |
runEndLoop(); | |
} else { | |
// wait | |
} | |
} | |
private boolean isMe(long userId) { | |
return getMe().getId() == userId; | |
} | |
private void showViewLoading() { | |
//TODO | |
} | |
private void hideViewLoading() { | |
//TODO | |
} | |
private void showViewRetry() { | |
//TODO | |
} | |
private void hideViewRetry() { | |
//TODO | |
} | |
private void showErrorMessage(String errorBundle) { | |
//TODO | |
} | |
private void createQuizAndStartToPLay() { | |
this.startQuizInteractor.execute(getMe().getId(), startQuizCallback); | |
} | |
// manage the interactor result | |
StartQuizInteractor.Callback startQuizCallback = new StartQuizInteractor.Callback() { | |
@Override | |
public void onLoaded(Quiz response) { | |
quizController.setUpQuiz(response); | |
quiz = response; | |
opponent = quizController.getOpponent(getMe()); | |
} | |
@Override | |
public void onError(String error) { | |
// show error message and retry | |
} | |
}; | |
public void runQuiz() { | |
Log.d("QUIZ", "runQuiz"); | |
runPromptLoop(); | |
} | |
private void runPromptLoop() { | |
cleanState(); | |
Log.d("QUIZ", "runPromptLoop"); | |
if (quizController.hasNextPrompt()) { | |
currentPrompt = quizController.getNextPrompt(); | |
Log.d("QUIZ", "starting countdown for step " + currentPrompt.getPosition() + " time " + | |
currentPrompt.getTimeout()); | |
prepareStartLoop(); | |
runStartLoop(); | |
} else { | |
finish(); | |
} | |
} | |
/** | |
* runs the prompt but with a time elapsed in seconds | |
* | |
* @param prompt | |
* @param fixedTime | |
*/ | |
private void runPromptLoop(Prompt prompt, long fixedTime) { | |
Log.d("QUIZ", "runPromptLoop"); | |
currentPrompt = prompt; | |
if (fixedTime < QuizConstats.START_LOOP_TIME_SECONDS) | |
runStartLoop(fixedTime); | |
else if (fixedTime < QuizConstats.START_LOOP_TIME_SECONDS + currentPrompt.getTimeout()) | |
runPlayTimer(fixedTime - QuizConstats.START_LOOP_TIME_SECONDS); | |
else if (fixedTime < QuizConstats.START_LOOP_TIME_SECONDS + currentPrompt.getTimeout() | |
+ QuizConstats.END_LOOP_TIME_SECONDS) | |
runEndLoop(fixedTime - QuizConstats.START_LOOP_TIME_SECONDS - currentPrompt.getTimeout()); | |
else { | |
// There are an error | |
} | |
} | |
/** | |
* start the Play prompt countdown without delay | |
*/ | |
private void runPlayTimer() { | |
runPlayTimer(0); | |
} | |
/** | |
* start the Play prompt countdown with fixedTime delay | |
* | |
* @param fixedTime : time it is supposed already pass | |
*/ | |
private void runPlayTimer(long fixedTime) { | |
currentLoop = QuizConstats.PLAY_LOOP; | |
loopAccumulatedTime = fixedTime; | |
preparePlayLoop(); | |
playTimer.start(); | |
} | |
/** | |
* start the Play start Prompt countdown | |
*/ | |
private void runStartLoop() { | |
runStartLoop(0); | |
} | |
/** | |
* start the Play start Prompt countdown with fixedTime delay | |
* | |
* @param fixedTime : time it is supposed already pass | |
*/ | |
private void runStartLoop(long fixedTime) { | |
currentLoop = QuizConstats.START_LOOP; | |
loopAccumulatedTime = fixedTime; | |
prepareStartLoop(); | |
startTimer.start(); | |
} | |
private void runEndLoop() { | |
runEndLoop(0); | |
} | |
/** | |
* start the end loop with fixedTime delay | |
* | |
* @param fixedTime : time it is supposed already pass | |
*/ | |
private void runEndLoop(long fixedTime) { | |
currentLoop = QuizConstats.END_LOOP; | |
loopAccumulatedTime = fixedTime; | |
prepareEndLoop(); | |
endTimer.start(); | |
} | |
/** | |
* Simulates the full prompt | |
* | |
* @param prompt | |
*/ | |
private void fastForwardPrompt(Prompt prompt) { | |
cleanState(); | |
currentPrompt = prompt; | |
fastForwardStartLoop((int) QuizConstats.START_LOOP_TIME_SECONDS); | |
fastForwardPlayLoop(currentPrompt.getTimeout()); | |
fastForwardEndLoop(QuizConstats.END_LOOP_TIME_SECONDS); | |
} | |
/** | |
* simulate the prompt until the tillTimeInSeconds | |
* | |
* @param prompt | |
* @param tillTimeInSeconds | |
*/ | |
private void fastForwardPrompt(Prompt prompt, long tillTimeInSeconds) { | |
cleanState(); | |
currentPrompt = prompt; | |
Log.d("QUIZ", "starting countdown for step " + currentPrompt.getPosition() + " time " + | |
currentPrompt.getTimeout()); | |
// should simulate full start loop | |
if (tillTimeInSeconds > QuizConstats.START_LOOP_TIME_SECONDS) { | |
// simulate the the full loop | |
fastForwardStartLoop((int) QuizConstats.START_LOOP_TIME_SECONDS); | |
tillTimeInSeconds -= (int) QuizConstats.START_LOOP_TIME_SECONDS; | |
} else { | |
// simulate until the time then return to start to play normally since now | |
fastForwardStartLoop(tillTimeInSeconds); | |
return; | |
} | |
// should simulate full start loop | |
if (tillTimeInSeconds > currentPrompt.getTimeout()) { | |
// simulate the the full loop | |
fastForwardPlayLoop(currentPrompt.getTimeout()); | |
tillTimeInSeconds -= currentPrompt.getTimeout(); | |
} else { | |
// simulate until the time then return to start to play normally since now | |
fastForwardPlayLoop(tillTimeInSeconds); | |
return; | |
} | |
// noting to simulate, last loop don nothing at now | |
if (tillTimeInSeconds > QuizConstats.END_LOOP_TIME_SECONDS) { | |
fastForwardEndLoop(tillTimeInSeconds); | |
} else { | |
// there are an error, | |
} | |
} | |
/** | |
* Prepares the Play loop, instantiates the countdown, and keep it ready to run | |
* | |
* @param tillTimeSeconds | |
*/ | |
private void fastForwardPlayLoop(long tillTimeSeconds) { | |
int promptPosition = currentPrompt.getPosition(); | |
opponentAnswered = (tillTimeSeconds >= quizController.getUserTime(opponent.getId(), promptPosition)) | |
|| tillTimeSeconds >= currentPrompt.getTimeout(); | |
userAnswered = (tillTimeSeconds >= quizController.getUserTime(getMe().getId(), promptPosition)) | |
|| tillTimeSeconds >= currentPrompt.getTimeout(); | |
if (userAnswered) { | |
view.updateResponse(quizController.getUserTextSelected(getMe().getId(), | |
promptPosition), | |
quizController.getUserTime(getMe().getId(), promptPosition), | |
quizController.wasRight(getMe().getId(), promptPosition), true); | |
view.renderBubble(); | |
} | |
if (opponentAnswered) { | |
view.updateOpponentResponse(quizController.getUserTextSelected(opponent.getId(), | |
promptPosition), | |
quizController.getUserTime(opponent.getId(), promptPosition), | |
quizController.wasRight(opponent.getId(), promptPosition), userAnswered); | |
view.renderOpponentBubble(); | |
} | |
} | |
private User getMe() { | |
return UserManager.getInstance().getMe(); | |
} | |
private void fastForwardStartLoop(long tillTimeInSec) { | |
if (tillTimeInSec >= 2l) { | |
showNewQuestionView(currentPrompt.getText()); | |
} | |
if (tillTimeInSec >= QuizConstats.START_LOOP_TIME_SECONDS ) { | |
createViewResponse(); | |
showNewOtionsView(currentPrompt.getOptions()); | |
} | |
} | |
private void fastForwardEndLoop(long tillTimeInSeconds) { | |
//Do nothing at now | |
} | |
/** | |
* Prepares the Play loop, instantiates the countdown, and keep it ready to run | |
*/ | |
private void preparePlayLoop() { | |
playTimer = new CountDownTimer((currentPrompt.getTimeout() * 1000) - (loopAccumulatedTime * 1000), 1000) { | |
public void onTick(long millisUntilFinished) { | |
if (!currentLoop.equals(QuizConstats.PLAY_LOOP)) | |
return; | |
loopAccumulatedTime++; | |
view.updateCountDown("seconds remaining: " + (currentPrompt.getTimeout() - loopAccumulatedTime) + "s"); | |
if (loopAccumulatedTime == quizController.getUserTime(opponent.getId(), currentPrompt.getPosition())) { | |
onOpponentSelected(currentPrompt.getPosition(), | |
quizController.getUserSelected(opponent.getId(), currentPrompt.getPosition())); | |
} | |
} | |
public void onFinish() { | |
if (!currentLoop.equals(QuizConstats.PLAY_LOOP)) | |
return; | |
loopAccumulatedTime++; | |
quizController.onTimeOut(currentPrompt.getPosition()); | |
onTimeOut(currentPrompt.getPosition()); | |
Log.d("QUIZ", "time out step " + currentPrompt.getPosition()); | |
view.updateCountDown("timeout!"); | |
} | |
}; | |
} | |
private void prepareEndLoop() { | |
endTimer = new CountDownTimer(QuizConstats.END_LOOP_TIME_MILLIS - loopAccumulatedTime * 1000, 1000) { | |
public void onTick(long millisUntilFinished) { | |
loopAccumulatedTime++; | |
} | |
public void onFinish() { | |
loopAccumulatedTime++; | |
runPromptLoop(); | |
} | |
}; | |
} | |
private void prepareStartLoop() { | |
startTimer = new CountDownTimer(QuizConstats.START_LOOP_TIME_MILLIS - loopAccumulatedTime * 1000, 1000) { | |
public void onTick(long millisUntilFinished) { | |
loopAccumulatedTime++; | |
if (loopAccumulatedTime == 2) { | |
showNewQuestionView(currentPrompt.getText()); | |
} | |
} | |
public void onFinish() { | |
loopAccumulatedTime++; | |
createViewResponse(); | |
showNewOtionsView(currentPrompt.getOptions()); | |
runPlayTimer(); | |
} | |
}; | |
} | |
/** | |
* clean the saved state to be able to start to play a new prompt | |
*/ | |
private void cleanState() { | |
view.cleanTimer(); | |
userAnswered = false; | |
opponentAnswered = false; | |
view.cleanPromptResult(); | |
view.hideOptions(); | |
loopAccumulatedTime = 0; | |
} | |
private void createViewResponse() { | |
view.createResponse(false); | |
view.createOpponentResponse(false); | |
view.renderBubble(); | |
view.renderOpponentBubble(); | |
} | |
private void showNewQuestionView(String text) { | |
view.showQuestion(text); | |
} | |
private void showNewOtionsView(List<PromptOption> options) { | |
// TODO do not pass the model to the view | |
// iterate every optoin and send one message per option to the view | |
view.updateButtons(currentPrompt); | |
} | |
private void finish() { | |
view.showFinishScreen(); | |
} | |
private void drawQuizFromThisState(GameState state) { | |
while (quizController.hasNextPrompt()) { | |
currentPrompt = quizController.getNextPrompt(); | |
if (state.shouldFullFastForward(currentPrompt)) { | |
fastForwardPrompt(currentPrompt); | |
} else { | |
// fastForward just part of the prompt | |
long promptAccumulatedTime = state.getAccumulatedTime(currentPrompt); | |
fastForwardPrompt(currentPrompt, promptAccumulatedTime); | |
runPromptLoop(currentPrompt, promptAccumulatedTime); | |
break; | |
} | |
} | |
} | |
@Override | |
public void resume() { | |
// this is the pause state recovered from saved instance. | |
if (gameState != null) { | |
this.getQuizInteractor.execute(getMe().getId(), new GetQuizInteractor.Callback() { | |
@Override | |
public void onLoaded(Quiz response) { | |
// manage the interactor result | |
quizController.setUpQuiz(response); | |
quiz = response; | |
opponent = quizController.getOpponent(getMe()); | |
//recover the state and simulate the time | |
Log.d("QUIZ", "cleaning screen"); | |
view.cleanHistory(); | |
// TODO, add the player responses here | |
int i = 0; | |
for (QuizResponse r : gameState.getUserAnswers()) { | |
i++; | |
quiz.addResponse(UserManager.getInstance().getMe().getId(), r, i); | |
} | |
gameState.forwadTo(System.currentTimeMillis(), quiz); | |
drawQuizFromThisState(gameState); | |
} | |
@Override | |
public void onError(String error) { | |
// show error message and retry | |
} | |
}); | |
} | |
} | |
@Override | |
public void pause() { | |
gameState = new GameState(); | |
gameState.setCurrentLoop(currentLoop); | |
gameState.setLoopAccumulatedTime(loopAccumulatedTime); | |
gameState.setOpponentAnswered(opponentAnswered); | |
gameState.setUserAnswered(userAnswered); | |
gameState.setQuizId(quiz.getId()); | |
gameState.setTimeStamp(System.currentTimeMillis()); | |
gameState.setCurrentPromptId(currentPrompt.getPosition()); | |
for (QuizResponse r : quiz.getResponses().get(getMe().getId()).values()) { | |
gameState.addUserResponse(r); | |
} | |
if (startTimer != null) | |
startTimer.cancel(); | |
if (playTimer != null) | |
playTimer.cancel(); | |
if (endTimer != null) | |
endTimer.cancel(); | |
Log.d("QUIZ", "on Pause " + gameState.toString()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment