/** * This file is part of Owlet. * * Owlet 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 3 of the License, or * (at your option) any later version. * * Owlet 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 Owlet. If not, see <http://www.gnu.org/licenses/>. */ package com.sibext.owlet.tasks; import java.util.ArrayList; import java.util.Observable; import java.util.Random; import com.sibext.owlet.OwletApplication; import com.sibext.owlet.R; import com.sibext.owlet.activity.ScreenOfTaskActivity; import com.sibext.owlet.helper.Log; import com.sibext.owlet.player.MediaPlayerSingleton; import com.sibext.owlet.view.BaseTaskImageView; import com.sibext.owlet.view.TaskImageFactory; import com.sibext.owlet.view.TaskImageView; import com.sibext.owlet.view.renderer.Renderer; import android.annotation.TargetApi; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.os.Build; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationSet; import android.view.animation.TranslateAnimation; import android.widget.RelativeLayout; import android.widget.TextView; abstract public class Task { public static final String TAG = "Task"; public final static int SYSTEMATISATION_TASK_NAME = R.string.systematisation; public final static int COMPARE_TASK_NAME = R.string.compare; public final static int MAGICSUARE_TASK_NAME = R.string.magic_square; public final static int CONCLUSION_TASK_NAME = R.string.conclusion; public final static int SYSTEMATISATION_TASK_TYPE = 1; public final static int COMPARE_TASK_TYPE = 2; public final static int MAGICSUARE_TASK_TYPE = 3; public final static int CONCLUSION_TASK_TYPE = 4; public final static int MAGICSUARE_TASK_LEGTH_DEFAULT = 4; public final static int SYSTEMATISATION_TASK_LEGTH_DEFAULT = 4; public final static int CONCLUSION_TASK_LEGTH_DEFAULT = 7; public final static int COMPARE_TASK_LEGTH_DEFAULT = 4; public final static int COMPLEXITY_LOW = 0; public final static int COMPLEXITY_MEDIUM = 1; public final static int COMPLEXITY_HIGH = 2; protected final int DISTANCE_BETWEEN_IMAGES = 5; private final int UNDEFINED_ID = -1; protected int widthTaskImage; protected int heightTaskImage; protected int marginTopTaskImage; protected int marginLeftTaskImage; protected int widthAnswerImage; protected int heightAnswerImage; protected int marginLeftAnswerImage; protected int marginTopAnswerImage; protected int type; protected int complexity = COMPLEXITY_LOW; protected int length; protected final int infoTextId; protected final int infoAudioId; protected Context context; protected int widthScreen; protected int heightScreen; protected int widthBoard; protected TaskObservable observer; protected int heightBoard; protected int failCount; protected TextView textView; protected MediaPlayerSingleton sound; private int imageGroupId; protected ArrayList<BaseTaskImageView> answers; protected ArrayList<TaskImageView> emptyCells; protected int[] location; protected int[] acceptColors; protected boolean colorFlag; protected int[] colors; protected Random rand; public Task(int type, int complexity, int length, int infoTextId) { this.type = type; this.complexity = complexity; this.length = length; this.infoTextId = infoTextId; this.infoAudioId = R.raw.task_compare_message; this.rand = new Random(); answers = new ArrayList<BaseTaskImageView>(); } public Task(int type, int length, int taskMessageId, Context context,TaskParamsContainer container) { this(type, container.getComplexity(), length, taskMessageId); this.context = context; this.complexity = getComplexityFromPref(); acceptColors = new int[]{ context.getResources().getColor(R.color.task_image_red), context.getResources().getColor(R.color.task_image_brown), context.getResources().getColor(R.color.task_image_dark_blue), context.getResources().getColor(R.color.task_image_lilac), context.getResources().getColor(R.color.task_image_yellow), context.getResources().getColor(R.color.task_image_gray), context.getResources().getColor(R.color.task_image_purple), context.getResources().getColor(R.color.task_image_white), }; this.widthBoard = container.getBoardParams()[ScreenOfTaskActivity.WIDTH_INDEX]; this.heightBoard = container.getBoardParams()[ScreenOfTaskActivity.HEIGTH_INDEX]; this.widthScreen = container.getWidthScreen(); this.heightScreen = container.getHeightScreen() - ScreenOfTaskActivity.statusBarHeigth; this.observer = new TaskObservable(); this.failCount=0; this.textView = container.getTextView(); this.observer.addObserver(container.getObserver()); this.sound = container.getSound(); } public class TaskObservable extends Observable { public void onTaskEnd(boolean result){ setChanged(); notifyObservers(result); } } public int getType() { return type; } public int getComplexity() { return complexity; } public int getFailCount() { return failCount; } public int getLiength() { return length; } public int getInfoTextId() { return infoTextId; } public int getInfoAudioId() { return infoAudioId; } public void setFailCount(int failCount){ this.failCount = failCount; } public void setType(int t) { type = t; } public void setComplexity(int c) { this.complexity = c; } public void setLiength(int l) { length = l; } public void removeAnswerViews(){ for(int i=0; i < answers.size(); i++){ answers.get(i).setVisibility(View.GONE); } } private int getComplexityFromPref(){ OwletApplication app = (OwletApplication)context.getApplicationContext(); SharedPreferences pref = app.getSettings(); return pref.getInt(type+OwletApplication.COMPLEX_POSTFIX, COMPLEXITY_LOW); } abstract protected Class<?>[] getCollectionOfView(); abstract public ViewGroup taskBuildView(final Context context, final ViewGroup layout, final RelativeLayout root); abstract public int getEveryFailCountLimit(); protected BaseTaskImageView taskImageViewFactory(Context context, int groupId, Renderer renderer) { try { Class<?>[] views = getCollectionOfView(); return TaskImageFactory.getImageView(views[groupId], context, renderer); } catch (Exception e) { Log.e(TAG, "Not found required constructor for task image view", e); } return null; } protected int getRandomId() { return rand.nextInt(getCollectionOfView().length); } protected int generateImageGroupId() { if(imageGroupId == UNDEFINED_ID){ imageGroupId = getRandomId(); } return imageGroupId; } /** * Chooses random group only one times * * @return the selected class of TaskImageView */ protected Class<?> getImageGroupClass() { imageGroupId = generateImageGroupId(); return getCollectionOfView()[imageGroupId]; } protected boolean getRandomColor() { return rand.nextBoolean(); } public void setImageGroupId(int id){ this.imageGroupId = id%getCollectionOfView().length; } public int getImageGroupId(){ return this.imageGroupId; } public abstract void playTaskMessage(MediaPlayerSingleton sound); /** * Возвращает OnTouchListener который при клике перемещает элемент на позицию елемента массива emptyCells * с индексом 0. При совпадении вариантов ответа этот элемент перерисовывается в перемещаемый элемент * и выталкивается из emptyCells, а перемещаемый элемент возвращается на исходное место, иначе * перемещаемый элемент просто возврвщатся обратно и массив emptyCells остается без изменений. * После возврата элемента на место, элементы на которые установлен данный OnTouchListener перемешиваются. * При опустошении массива emptyCells вызывается метод "onTaskEnd(boolean)", с параметром, соответствующим * результату выполнения текущего задания. * @param emptyCells * @return */ protected OnTouchListener getMovebleListener(final ArrayList<TaskImageView> emptyCells){ return new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (emptyCells.size() != 0) { if (event.getAction() == MotionEvent.ACTION_DOWN) { final BaseTaskImageView img = (BaseTaskImageView) v; if(img.getEnabledFlag()){ final TaskImageView goal = emptyCells.get(0); int delta = (int) ((widthAnswerImage - widthTaskImage) / 2); float toX = goal.getXcoordinate() - img.getLeftMargin() - delta; float toY = goal.getYcoordinate() - img.getTopMargin() - delta - ScreenOfTaskActivity.statusBarHeigth; TranslateAnimation animation = new TranslateAnimation(0, toX, 0, toY); animation.setDuration(700); TranslateAnimation backAnim = new TranslateAnimation(0, -toX, 0, -toY); backAnim.setStartOffset(700); backAnim.setDuration(200); backAnim.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { for(int i=0;i<answers.size();i++){ answers.get(i).setEnabledFlag(true); } shakeAnswer(); } }); // TranslateAnimation backAnimfast = new TranslateAnimation(0, -toX, 0, -toY); backAnimfast.setStartOffset(700); backAnimfast.setDuration(50); backAnimfast.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { for(int i=0;i<answers.size();i++){ answers.get(i).setEnabledFlag(true); } setVisible(img, true); shakeAnswer(); } }); if (img.getVariantAnswer() == emptyCells.get(0) .getCorrectAnswer()) { final Bitmap bm = img.getDrawingCache(); animation.setAnimationListener(new AnimationListener() { public void onAnimationStart(Animation animation) { for(int i=0;i<answers.size();i++){ answers.get(i).setEnabledFlag(false); } emptyCells.remove(0); } public void onAnimationRepeat(Animation animation) { } public void onAnimationEnd(Animation animation) { goal.setupBackground(context, 0); goal.setImageBitmap(bm); textView.setText(context.getString(R.string.dialog_title_good_work)); if (emptyCells.size() == 0){ observer.onTaskEnd(true); } else { sound.play(R.raw.dialog_title_correct_answer); } setQuestionImageOnHead(emptyCells); setVisible(img, false); } }); AnimationSet set = new AnimationSet(context, null); set.addAnimation(animation); set.addAnimation(backAnimfast); if(OwletApplication.VERSION<=Build.VERSION_CODES.GINGERBREAD_MR1){ img.startAnimation(set); } else { if(emptyCells.size()==1){ observer.onTaskEnd(true); } else { img.startAnimation(set); } } } else { animation.setAnimationListener(new AnimationListener() { public void onAnimationStart(Animation animation) { for(int i=0;i<answers.size();i++){ answers.get(i).setEnabledFlag(false); } } public void onAnimationRepeat(Animation animation) { } public void onAnimationEnd(Animation animation) { textView.setText(context.getString(R.string.dialog_title_incorrect_answer)); if(failCount>getEveryFailCountLimit()){ observer.onTaskEnd(false); } else { Random random = new Random(); boolean say = random.nextBoolean(); if(say) sound.play(R.raw.dialog_title_incorrect_answer); } } }); AnimationSet set = new AnimationSet(context, null); set.addAnimation(animation); set.addAnimation(backAnim); if(OwletApplication.VERSION<=Build.VERSION_CODES.GINGERBREAD_MR1){ failCount++; img.startAnimation(set); } else { failCount++; if(failCount>getEveryFailCountLimit()){ observer.onTaskEnd(false); } else { img.startAnimation(set); } } } } } } return false; } }; } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void setVisible(BaseTaskImageView img, boolean isFull){ if(OwletApplication.VERSION>=Build.VERSION_CODES.HONEYCOMB){ if(isFull) { img.setAlpha((float)1); } else { img.setAlpha((float)0); } } else { if(isFull) { img.setAlpha(255); } else { img.setAlpha(0); } } } private void shakeAnswer(){ int answersCount = answers.size(); int[] position = new int[answersCount]; position = selectImages(answersCount, answersCount); for (int i = 0; i < answersCount; i++) { TaskImageView img = answers.get(i); RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)img.getLayoutParams(); params.leftMargin = position[i] * (widthAnswerImage + 2*DISTANCE_BETWEEN_IMAGES) + marginLeftAnswerImage; params.topMargin = marginTopAnswerImage; img.setLeftMargin(params.leftMargin); img.setTopMargin(params.topMargin); img.setLayoutParams(params); } } protected int[] selectImages(int length, int max_value) { ArrayList<Integer> allImages = new ArrayList<Integer>(); for(int i=0;i<max_value;i++){ allImages.add(i); } Random generator = new Random(); int[] images = new int[length]; int n; for(int i = 0; i < length; i++) { n = generator.nextInt(allImages.size()); images[i] = allImages.get(n); allImages.remove(n); } return images; } protected int[] selectColors(int length){ ArrayList<Integer> allImages = new ArrayList<Integer>(); for(int i=0;i<acceptColors.length;i++){ allImages.add(acceptColors[i]); } Random generator = new Random(); int[] images = new int[length]; int n; for(int i = 0; i < length; i++) { n = generator.nextInt(allImages.size()); images[i] = allImages.get(n); allImages.remove(n); } return images; } protected void setQuestionImageOnHead(ArrayList<TaskImageView> emptyCells){ if(emptyCells.size() == 0){ return; } int resId = R.drawable.question; emptyCells.get(0).setupBackground(context, resId); } }