Skip to content

Instantly share code, notes, and snippets.

@marinoluck
Last active August 29, 2015 14:23
Show Gist options
  • Save marinoluck/836468a8f6010e9aca91 to your computer and use it in GitHub Desktop.
Save marinoluck/836468a8f6010e9aca91 to your computer and use it in GitHub Desktop.
Quiz Presenter
/**
* Presenter created to show the quiz flow on a view.
* Manages the Quiz LifeCycle
*
* @author Lucas Marino mailto:marinoluck@gmail.com.
*/
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