package com.geek_alarm.android.activities;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;
import com.geek_alarm.android.AlarmPreference;
import com.geek_alarm.android.Player;
import com.geek_alarm.android.R;
import com.geek_alarm.android.Utils;
import com.geek_alarm.android.db.AlarmPreferenceDao;
import com.geek_alarm.android.db.TaskTypeDao;
import com.geek_alarm.android.tasks.Task;
import com.geek_alarm.android.tasks.TaskManager;
import com.geek_alarm.android.tasks.TaskType;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* Activity, it is place where user solves tasks.
*/
public class TaskActivity extends Activity {
private boolean waitingForTask;
private boolean testTask;
private Queue<Task> availableTasks;
private ChoiceListener choiceListener;
private int correctChoiceId;
private TaskLoader loader;
//private MediaPlayer player;
private Player player;
private int solved;
// How many tasks user already tried: solved + unsolved.
private int all;
private LayoutInflater inflater;
private LinearLayout layout;
private Task currentTask;
private int numberOfAttempts;
private int positiveBalance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().getData() == null) {
testTask = true;
} else if (!containsToday()) {
finish();
return;
}
numberOfAttempts = Utils.getNumberOfAttempts();
positiveBalance = Utils.getPositiveBalance();
inflater = getLayoutInflater();
layout = (LinearLayout) inflater.inflate(R.layout.task, null);
setContentView(layout);
player = new Player(this);
choiceListener = new ChoiceListener();
availableTasks = new LinkedList<Task>();
waitingForTask = true;
Utils.updateTaskTypesAsync(true);
runTaskLoader();
findViewById(R.id.mute_button).setOnClickListener(new MuteListener());
findViewById(R.id.info_button).setOnClickListener(new InfoListener());
updateStats();
WindowManager.LayoutParams layout = getWindow().getAttributes();
layout.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
layout.screenBrightness = 0.1F;
getWindow().setAttributes(layout);
}
private void runTaskLoader() {
loader = new TaskLoader();
loader.execute();
}
/**
* Checks, if today's day is enable in current alarm.
* @return true or false (amazing, really?).
*/
private boolean containsToday() {
int id = Integer.parseInt(getIntent().getData()
.getEncodedSchemeSpecificPart());
AlarmPreference alarm = AlarmPreferenceDao.INSTANCE.findById(id);
Calendar cal = Calendar.getInstance();
int today = cal.get(Calendar.DAY_OF_WEEK);
return (alarm.getDays() & (1 << Utils.getDayOfWeek(today))) != 0;
}
/**
* Shows (or removes) error message with given id.
* @param errorMessageId resource id of message, -1 if no error.
*/
private void showErrorMessage(int errorMessageId) {
TextView errorView = (TextView) findViewById(R.id.error_message);
if (errorMessageId == -1) {
errorView.setVisibility(View.GONE);
} else {
errorView.setText(errorMessageId);
errorView.setVisibility(View.VISIBLE);
}
}
private void toggleSpinner(boolean show) {
findViewById(R.id.progress).setVisibility(
show ? View.VISIBLE : View.GONE);
findViewById(R.id.task_question).setVisibility(
show ? View.GONE : View.VISIBLE);
}
private void displayTask(Task task) {
toggleSpinner(false);
showErrorMessage(task.getErrorMessageId());
ImageView question = (ImageView) findViewById(R.id.task_question);
int choiceWidth = task.getChoice(0).getWidth();
Display display = getWindowManager().getDefaultDisplay();
// If images is too wide, we need to use layout 4x1 instead of table 2x2.
boolean isTable = choiceWidth * 2 + 10 < display.getWidth();
float weight = 0.55f;
int minHeight = (int) (layout.getBottom() * (1 - weight) / 4);
layout.addView(inflater.inflate(isTable ? R.layout.choices_table
: R.layout.choices_list, null), new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, weight));
question.setImageBitmap(Utils.resizeImage(task.getQuestion()));
int choicesIds[] = { R.id.task_choice_1, R.id.task_choice_2,
R.id.task_choice_3, R.id.task_choice_4 };
correctChoiceId = choicesIds[task.getCorrect()];
currentTask = task;
for (int i = 0; i < 4; i++) {
ImageView choiceView = (ImageView) findViewById(choicesIds[i]);
choiceView.setOnClickListener(choiceListener);
choiceView.setImageBitmap(Utils.resizeImage(task.getChoice(i)));
if (!isTable) {
choiceView.getLayoutParams().height = Math.max(minHeight, task
.getChoice(i).getHeight());
}
}
}
private void updateStats() {
TextView solvedView = (TextView) findViewById(R.id.solved);
solvedView.setText(String.format("%d/%d", 2 * solved - all,
positiveBalance));
TextView leftView = (TextView) findViewById(R.id.left);
leftView.setText(String.valueOf(numberOfAttempts - all));
}
@Override
public void onBackPressed() {
if (testTask) {
super.onBackPressed();
}
}
@Override
public void onDestroy() {
super.onDestroy();
player.destroy();
loader.cancel(false);
WindowManager.LayoutParams layout = getWindow().getAttributes();
layout.screenBrightness = -1F;
getWindow().setAttributes(layout);
}
/**
* Loads numberOfAttempts of random tasks from server.
*/
private class TaskLoader extends AsyncTask<Void, Task, Void> {
@Override
protected Void doInBackground(Void... params) {
if (Utils.isOnline()) {
List<TaskType> taskTypes = removeDisabledTasks(TaskTypeDao.INSTANCE.getAll());
if (!taskTypes.isEmpty()) {
downloadTasks(taskTypes);
} else {
generateSimpleTasks(TaskType.Level.MEDIUM.getValue(), R.string.server_error);
}
} else {
generateSimpleTasks(TaskType.Level.MEDIUM.getValue(), R.string.not_online);
}
return null;
}
private void generateSimpleTasks(int difficulty, int errorMessageId) {
for (int i = 0; i < numberOfAttempts; i++) {
Task task = TaskManager.generateSimpleTask(difficulty);
task.setErrorMessageId(errorMessageId);
publishProgress(task);
}
}
private List<TaskType> removeDisabledTasks(List<TaskType> taskTypes) {
List<TaskType> result = new ArrayList<TaskType>();
for (TaskType type : taskTypes) {
if (type.getLevel() != TaskType.Level.NONE) {
result.add(type);
}
}
return result;
}
private void downloadTasks(List<TaskType> taskTypes) {
Collections.shuffle(taskTypes);
for (int i = 0; i < numberOfAttempts; i++) {
TaskType type = taskTypes.get(i % taskTypes.size());
Task task;
try {
task = TaskManager.getTask(type);
task.setErrorMessageId(-1);
} catch (Exception e) {
// Some error, so we show simple task.
Log.e(TaskLoader.class.getName(), "Something bad", e);
task = TaskManager.generateSimpleTask(type.getLevel().getValue());
task.setErrorMessageId(R.string.server_error);
}
publishProgress(task);
if (isCancelled()) {
return;
}
}
}
@Override
protected void onProgressUpdate(Task... values) {
for (Task task : values) {
availableTasks.add(task);
}
if (waitingForTask) {
player.start();
Task task = availableTasks.poll();
displayTask(task);
waitingForTask = false;
}
}
}
private class ChoiceListener implements View.OnClickListener {
@Override
public void onClick(View v) {
all++;
solved += v.getId() == correctChoiceId ? 1 : 0;
new ResultSender(currentTask.getId(), v.getId() == correctChoiceId)
.execute();
if (2 * solved - all == positiveBalance || all == numberOfAttempts) {
boolean win = 2 * solved - all == positiveBalance;
if (!testTask) {
Intent intent = new Intent(TaskActivity.this, ResultActivity.class);
intent.putExtra("win", win);
intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
startActivity(intent);
}
TaskActivity.this.finish();
return;
}
layout.removeViewAt(layout.getChildCount() - 1);
updateStats();
Toast.makeText(getApplicationContext(),
v.getId() == correctChoiceId ? "Accepted" : "Wrong answer",
Toast.LENGTH_SHORT).show();
if (availableTasks.isEmpty()) {
if (loader.getStatus() == AsyncTask.Status.FINISHED) {
loader = new TaskLoader();
loader.execute();
}
waitingForTask = true;
toggleSpinner(true);
} else {
displayTask(availableTasks.poll());
}
}
}
private class MuteListener implements View.OnClickListener {
@Override
public void onClick(View v) {
player.mute();
}
}
private class InfoListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (currentTask == null) {
return;
}
SpannableString info = new SpannableString(currentTask.getType().getDescription());
Linkify.addLinks(info, Linkify.ALL);
AlertDialog dialog = new AlertDialog.Builder(TaskActivity.this)
.setTitle(currentTask.getType().getName())
.setMessage(info)
.setNeutralButton(R.string.hide, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}})
.show();
((TextView)dialog.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
}
}
private class ResultSender extends AsyncTask<Void, Void, Void> {
private String id;
private boolean solved;
public ResultSender(String id, boolean solved) {
this.id = id;
this.solved = solved;
}
@Override
protected Void doInBackground(Void... params) {
try {
TaskManager.addResult(id, solved);
} catch (Exception e) {
Log.e(TaskLoader.class.getName(), "Couldn't send result", e);
}
return null;
}
}
}