commit 2df492c79c384a12e272e8e38f3d58678c85a4db Author: Mysaa Date: Tue May 18 22:01:26 2021 +0200 Premier commit - Mise en place de git sur le projet. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9149f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +MCQFormQuizSheetParser/bin/ +app/build +MCQFormQuizSheetParser/.settings + +gradle/ +gradlew +gradlew.bat + +MCQFormQuizSheetParser/.classpath +MCQFormQuizSheetParser/.project + +out.log diff --git a/MCQFormQuizSheetParser/jOpenDocument-1.2.jar b/MCQFormQuizSheetParser/jOpenDocument-1.2.jar new file mode 100644 index 0000000..8305bfa Binary files /dev/null and b/MCQFormQuizSheetParser/jOpenDocument-1.2.jar differ diff --git a/MCQFormQuizSheetParser/src/com/bernard/mcq/formQuiz/MainParser.java b/MCQFormQuizSheetParser/src/com/bernard/mcq/formQuiz/MainParser.java new file mode 100644 index 0000000..98dcb70 --- /dev/null +++ b/MCQFormQuizSheetParser/src/com/bernard/mcq/formQuiz/MainParser.java @@ -0,0 +1,214 @@ +package com.bernard.mcq.formQuiz; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileNameExtensionFilter; + +import org.jdom.Element; +import org.jopendocument.dom.spreadsheet.Cell; +import org.jopendocument.dom.spreadsheet.Sheet; +import org.jopendocument.dom.spreadsheet.SpreadSheet; + +public class MainParser { + + static JFileChooser inputChooser; + static JFileChooser outputChooser; + + public static final byte[] appBernardKey = { 0b00100010, 0b01001001 }; + public static final byte[] bernardSignature = { 0b00011001, 0b01101101 }; + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, FileNotFoundException { + + System.setOut(new PrintStream(new FileOutputStream("out.log"))); + + inputChooser = new JFileChooser(); + inputChooser.addChoosableFileFilter(new FileNameExtensionFilter("Libre office calc sheet", "ods")); + while (true) { + int returnVal = inputChooser.showOpenDialog(null); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File inputFile = inputChooser.getSelectedFile(); + outputChooser = new JFileChooser(); + outputChooser.addChoosableFileFilter(new FileNameExtensionFilter("Bernard file", "bernard")); + int returnVal2 = outputChooser.showSaveDialog(null); + if (returnVal2 == JFileChooser.APPROVE_OPTION) { + String outputName = outputChooser.getSelectedFile().getName(); + outputName += !outputName.endsWith(".bernard") ? ".bernard" : ""; + File saveDirectory = outputChooser.getCurrentDirectory(); + String outQuizID; + do { + outQuizID = JOptionPane.showInputDialog(null, "What is the quiz's quizID ?", "QuizID ?", + JOptionPane.QUESTION_MESSAGE); + } while (!outQuizID.matches("[0-9]+")); + int quizID = Integer.parseInt(outQuizID); + String name = JOptionPane.showInputDialog(null, "What is the quiz's name ?", "Name ?", + JOptionPane.QUESTION_MESSAGE); + String author = JOptionPane.showInputDialog(null, "Who is the quiz's author ?", "Author ?", + JOptionPane.QUESTION_MESSAGE); + Sheet sheet; + try { + sheet = SpreadSheet.createFromFile(inputFile).getSheet(0); + + int row = 0; + List formsMarkers = new ArrayList<>(); + while (true) { + Cell cell = sheet.getCellAt(0, row); + System.out.println("\"" + cell.getTextValue() + "\""); + String tv = getCellContent(cell); + if (tv == null || tv.isEmpty()) + break; + formsMarkers.add(cell.getTextValue()); + row++; + } + String endTag = formsMarkers.get(formsMarkers.size() - 1); + formsMarkers.remove(formsMarkers.size() - 1); + + List>> verben = new ArrayList<>(); + + int col = 1; + int formID; + int tempFormID; + while (!sheet.getCellAt(col, 0).getTextValue().isEmpty()) { + //New Item + row = 0; + formID = -1; + Map> forms = new HashMap<>(); + + String cellValue; + while (!(cellValue = getCellContent(sheet.getCellAt(col,row))).equals(endTag)) { + + if ((tempFormID = formsMarkers.indexOf(cellValue))!=-1){ + formID = tempFormID; + forms.put(formID, new ArrayList()); + }else if (cellValue != null && !cellValue.isEmpty() && forms.get(formID) != null) + forms.get(formID).add(cellValue); + row++; + System.out.println("Future test : ("+col+";"+row+")"); + } + verben.add(forms); + col++; + } + + System.out.println("Final : "); + System.out.println(verben.toString()); + + // Save as quiz file + File saveFile = new File(saveDirectory, outputName); + if (saveFile.exists()) { + int returnVal3 = JOptionPane.showConfirmDialog(null, + "Do you really want to overwrite " + saveFile.getPath(), "Overwrite ?", + JOptionPane.YES_NO_OPTION); + if (returnVal3 != JOptionPane.YES_OPTION) + continue; + saveFile.delete(); + } + if (saveFile.canWrite()) + error(saveFile.getAbsolutePath() + " is unwritable !!!"); + if (saveFile.isDirectory()) + error(saveFile.getAbsolutePath() + " is a directory !!!"); + if (!saveFile.exists()) { + saveFile.createNewFile(); + } + DataOutputStream dos = new DataOutputStream(new FileOutputStream(saveFile)); + + // Writing Bernard signature + dos.write(bernardSignature); + + // Writing Bernard key + dos.write(appBernardKey); + + //QuizMetadata + + dos.writeInt(name.getBytes().length); + dos.write(name.getBytes()); + dos.writeInt(author.getBytes().length); + dos.write(author.getBytes()); + dos.writeLong(0); + dos.writeInt(quizID); + dos.writeBoolean(false); + + //QuizPayload + + // Writing quizType key + dos.writeInt(quizID); + + // Writing quiz + dos.writeInt(verben.size()); + for (Map> forms : verben) { + dos.writeInt(forms.size()); + for (Map.Entry> entry : forms.entrySet()) { + if (entry.getValue().size() <= 0) { + dos.writeInt(-1);//FormID + dos.writeInt(0); + System.err.println("This clause should not be executed"); + } else { + dos.writeInt(entry.getKey()); + dos.writeInt(entry.getValue().get(0).getBytes(UTF_8).length); + dos.write(entry.getValue().get(0).getBytes(UTF_8)); + dos.writeInt(entry.getValue().size() - 1); + for (int i = 1; i < entry.getValue().size(); i++) { + dos.writeInt(entry.getValue().get(i).getBytes(UTF_8).length); + dos.write(entry.getValue().get(i).getBytes(UTF_8)); + } + } + + } + } + + dos.close(); + + int resultVal4 = JOptionPane.showConfirmDialog(null, + "Fichier �crit !!! Voulez vous parser un autre document ?", "Done", + JOptionPane.YES_NO_OPTION); + + if (resultVal4 != JOptionPane.YES_OPTION) + break; + + } catch (IOException e) { + error("Impossible de lire le fichier , " + e.getClass().getName()); + } + + } else { + System.out.println("Bye !"); + return; + } + + } else { + System.out.println("Bye !"); + return; + } + } + + } + public static String getCellContent(Cell cell){ + try{ + //String toString = new String(((Element)cell.getElement().getContent(0)).getContent(0).getValue().getBytes(UTF_8),UTF_8); + //System.out.println(toString + Arrays.toString(toString.getBytes(UTF_8))); + return cell.getTextValue();//toString; + }catch(Exception e){ + System.err.println("(Un)expected Exception : " + e.getClass().getName()); + } + return null; + } + + public static void error(String text) { + System.out.println(text); + JOptionPane.showMessageDialog(null, text, "Error !!!!!", JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + +} diff --git a/MCQinator.svg b/MCQinator.svg new file mode 100644 index 0000000..93c021d --- /dev/null +++ b/MCQinator.svg @@ -0,0 +1,88 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..5978418 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.bernard.mcqinator" + minSdkVersion 15 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:28.0.0' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..cc0296f --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/EditQuizDEVerben.old b/app/src/main/java/com/bernard/mcqinator/EditQuizDEVerben.old new file mode 100644 index 0000000..3ce719a --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/EditQuizDEVerben.old @@ -0,0 +1,98 @@ +package com.bernard.qcminator.editQuiz; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ListView; + +import com.bernard.qcminator.ChoosingQuizToEditActivity; +import com.bernard.qcminator.R; +import com.bernard.qcminator.quiz.DEVerbenQuiz; +import com.bernard.qcminator.quiz.Quiz; + +import java.io.IOException; + +public class EditQuizDEVerben extends ListActivity { + + public static final int VERBE_NEW = 0x5603B6BD; + public static final int VERBE_CHANGE = 0x68E0B3A1; + + DEVerbenQuiz quiz; + DEVerbenQuiz.VerbeAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_edit_quiz_deverben); + Intent i = getIntent(); + int fileID = i.getIntExtra(ChoosingQuizToEditActivity.QUIZ_FILEID,-1); + Quiz q; + try { + q = Quiz.getFromFile(this.openFileInput("quiz_"+fileID+".bernard"),fileID); + } catch (IOException e) { + Log.e("Data error","Quiz filename invalid",e); + finish(); + return; + } + if(!(q instanceof DEVerbenQuiz)){ + Log.e("Data error","Quiz type invalid"); + finish(); + return; + } + quiz = (DEVerbenQuiz)q; + adapter = quiz.new VerbeAdapter(this); + setListAdapter(adapter); + + Button neueVerbe = (Button)findViewById(R.id.neueVerbeButton); + neueVerbe.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(EditQuizDEVerben.this, EditQuizDEVerbenVerbe.class); + i.putExtra(EditQuizDEVerbenVerbe.TO_EDIT_VERBE,(Parcelable) null); + startActivityForResult(i,VERBE_NEW); + } + }); + + Button doneButton = (Button)findViewById(R.id.doneButton); + doneButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + quiz.saveQuiz(EditQuizDEVerben.this); + finish(); + } + }); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if(requestCode == VERBE_NEW && resultCode == RESULT_OK){ + DEVerbenQuiz.Item v = data.getParcelableExtra(EditQuizDEVerbenVerbe.EDITED_VERBE); + quiz.addItem(v); + quiz.saveQuiz(this); + adapter = quiz.new VerbeAdapter(this); + setListAdapter(adapter); + } + if(requestCode == VERBE_CHANGE && resultCode == RESULT_OK){ + DEVerbenQuiz.Item v = data.getParcelableExtra(EditQuizDEVerbenVerbe.EDITED_VERBE); + int pos = data.getIntExtra(EditQuizDEVerbenVerbe.VERBE_POSITION,0); + quiz.setItem(pos,v); + quiz.saveQuiz(this); + adapter = quiz.new VerbeAdapter(this); + setListAdapter(adapter); + } + + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Intent i = new Intent(EditQuizDEVerben.this, EditQuizDEVerbenVerbe.class); + i.putExtra(EditQuizDEVerbenVerbe.TO_EDIT_VERBE,adapter.getItem(position)); + i.putExtra(EditQuizDEVerbenVerbe.VERBE_POSITION,position); + startActivityForResult(i,VERBE_CHANGE); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/api/IdentifiedQuiz.java b/app/src/main/java/com/bernard/mcqinator/api/IdentifiedQuiz.java new file mode 100644 index 0000000..ed6fbdd --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/IdentifiedQuiz.java @@ -0,0 +1,32 @@ +package com.bernard.mcqinator.api; + +import com.bernard.mcqinator.api.fragen.Frage; + +/** + * @author samy + * @date on 22/07/17. + */ + +public final class IdentifiedQuiz { + + private final F[] fragen; + private final I identifier; + + public IdentifiedQuiz(F[] fragen, I identifier) { + this.fragen = fragen; + this.identifier = identifier; + } + + public F[] getFragen() { + return fragen; + } + + public F getFrage(int pos) { + return fragen[pos]; + } + + public I getIdentifier() { + return identifier; + } + +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/QuizMetadata.java b/app/src/main/java/com/bernard/mcqinator/api/QuizMetadata.java new file mode 100644 index 0000000..8817433 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/QuizMetadata.java @@ -0,0 +1,181 @@ +package com.bernard.mcqinator.api; + +import android.app.Activity; +import android.support.annotation.Nullable; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.fragenStats.FormQuizStat; +import com.bernard.mcqinator.api.fragenStats.QuizStat; +import com.bernard.mcqinator.api.quiz.DEVerbenQuiz; +import com.bernard.mcqinator.api.quiz.Quiz; +import com.bernard.mcqinator.view.activities.congratsActivity.DEVerbenCongratsActivity; +import com.bernard.mcqinator.view.quizEditing.formQuiz.EditFormQuiz; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; + +public class QuizMetadata implements Serializable { + + + + private String name; + private String author; + private long fileID; + private Type type; + private boolean editable; + + public QuizMetadata(String name, String author, long fileID, Type type, boolean editable) { + this.name = name; + this.author = author; + this.fileID = fileID; + this.type = type; + this.editable = editable; + } + + public QuizMetadata() {}// For serialization + + + //////////// File methods //////////// + + public void read(DataInputStream fin) throws IOException{ + byte[] nameBytes = new byte[fin.readInt()]; + if(fin.read(nameBytes) != nameBytes.length)throw new IOException("Corrupted data : announced name bytes count dont match found"); + this.name = new String(nameBytes); + byte[] authorBytes = new byte[fin.readInt()]; + if(fin.read(authorBytes) != authorBytes.length)throw new IOException("Corrupted data : announced author bytes count dont match found"); + this.author = new String(authorBytes); + this.fileID = fin.readLong(); + this.type = Type.getTypeFromID(fin.readInt()); + this.editable = fin.readBoolean(); + } + + public void write(DataOutputStream fos) throws IOException{ + fos.writeInt(this.name.getBytes().length); + fos.write(this.name.getBytes()); + fos.writeInt(this.author.getBytes().length); + fos.write(this.author.getBytes()); + fos.writeLong(this.fileID); + fos.writeInt(this.type.getId()); + fos.writeBoolean(this.editable); + } + + + //////////// Getters and Setters //////////// + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public long getFileID() { + return fileID; + } + + public void setFileID(long fileID) { + this.fileID = fileID; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public boolean isEditable() { + return editable; + } + + public void setEditable(boolean editable) { + this.editable = editable; + } + + + //////////// Internal Classes //////////// + + public enum Type{ + DEVerben(DEVerbenQuiz.class, FormQuizStat.class, EditFormQuiz.class, DEVerbenCongratsActivity.class, R.drawable.de_verben_quiz,0); + + final Class quizClass; + final Class quizStatClass; + final Class editActivityClass; + final Class congratsActivityClass; + final int quizIconID; + final int id; + + Type(Class quizClass, Class quizStatClass, Class editActivityClass, Class congratsActivityClass, int quizIconID, int id) { + this.quizClass = quizClass; + this.quizStatClass = quizStatClass; + this.editActivityClass = editActivityClass; + this.congratsActivityClass = congratsActivityClass; + this.quizIconID = quizIconID; + this.id = id; + } + + Type(Class quizClass, Class quizStatClass, Class editActivityClass, Class congratsActivityClass, int id) { + this(quizClass,quizStatClass,editActivityClass,congratsActivityClass,R.drawable.default_quiz_icon,id); + } + + public Class getQuizClass() { + return quizClass; + } + + public Class getQuizStatClass() { + return quizStatClass; + } + + public Class getEditActivityClass() { + return editActivityClass; + } + + public Class getCongratsActivityClass(){ + return congratsActivityClass; + } + + public int getQuizIconID() { + return quizIconID; + } + + public int getId() { + return id; + } + + @Nullable + public static Type getTypeFromID(int id){ + for (Type t:Type.values()) { + if(t.getId() == id) + return t; + } + return null; + } + } + + + //////////// Misc /////////// + + @Override + public String toString() { + return "QuizMetadata{" + + "name='" + name + '\'' + + ", author='" + author + '\'' + + ", fileID=" + fileID + + ", type=" + type + + ", editable=" + editable + + '}'; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/api/frageable/BlocFrageable.java b/app/src/main/java/com/bernard/mcqinator/api/frageable/BlocFrageable.java new file mode 100644 index 0000000..f7c7c32 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/frageable/BlocFrageable.java @@ -0,0 +1,249 @@ +package com.bernard.mcqinator.api.frageable; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.InputType; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.fragen.BlocFrage; +import com.bernard.mcqinator.api.fragen.Frage; +import com.bernard.mcqinator.api.fragenStats.QuizStat; +import com.bernard.mcqinator.view.activities.MainActivity; +import com.bernard.mcqinator.view.activities.fragenActivities.BlocFrageActivity; +import com.bernard.mcqinator.view.activities.fragenActivities.FrageActivity; + +/** + * @author Mysaa + * @date on 05/05/17. + */ + +public interface BlocFrageable extends Frageable { + + int[] useableBlocCount = {2,3,4,6,8}; + Class CONFIGURATOR = BlocConfigurator.class; + String LAST_BLOC_VALUE = "com.bernard.qcminator.LAST_BLOC_VALUE"; + String LAST_FRAGE_COUNT = "com.bernard.qcminator.LAST_BLOC_FRAGE_COUNT"; + int DEFAULT_FRAGE_COUNT = 10; + int DEFAULT_FRAGE_COUNT_MAX = 50; + int DEFAULT_BLOC_VALUE = 2; + + class BlocData extends Frageable.Data{ + public final int blockNumber; + public final int length; + + + public BlocData(int blockNumber, int length) { + this.blockNumber = blockNumber; + this.length = length; + } + + public int getBlockNumber() { + return blockNumber; + } + + public int getLength() { + return length; + } + + @Override + public Class> getFrageActivityClass() { + return BlocFrageActivity.class; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(blockNumber); + dest.writeInt(length); + } + + public static Parcelable.Creator CREATOR = new Parcelable.Creator(){ + @Override + public BlocData createFromParcel(Parcel source) { + return new BlocData(source.readInt(),source.readInt()); + } + + @Override + public BlocData[] newArray(int size) { + return new BlocData[size]; + } + }; + } + + class BlocConfigurator extends Frageable.Configurator{ + public int lastBlocCountIndex = 0; + + @Override + public View attachConfigLayout(final Activity a,ViewGroup frame) { + final View layout = a.getLayoutInflater().inflate(R.layout.bloc_frage_options_layout,frame); + final Button blocCountButton = layout.findViewById(R.id.blocCount); + final TextView frageCountText = layout.findViewById(R.id.frageCountText); + final SeekBar frageCountBar = layout.findViewById(R.id.frageCount); + + SharedPreferences preferences = a.getSharedPreferences(MainActivity.PREFERENCES_FILE_KEY,Context.MODE_PRIVATE); + lastBlocCountIndex = preferences.getInt(LAST_BLOC_VALUE,DEFAULT_BLOC_VALUE); + int realBlocCount = (lastBlocCountIndex>=0)?useableBlocCount[lastBlocCountIndex]:-lastBlocCountIndex; + + blocCountButton.setText(a.getResources().getString(R.string.blocPerFrage,Integer.toString(realBlocCount,10))); + + blocCountButton.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + AlertDialog.Builder builder = new AlertDialog.Builder(a); + builder.setTitle(R.string.blocCountAsk); + final EditText blocCountPrompt = new EditText(a); + blocCountPrompt.setInputType(InputType.TYPE_CLASS_NUMBER); + builder.setView(blocCountPrompt); + builder.setPositiveButton(android.R.string.ok,new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + try { + int n = Integer.parseInt(blocCountPrompt.getText().toString(), 10); + lastBlocCountIndex = -n; + }catch(Exception e){ + Log.e("BlocCountDialog","Cannot read data",e); + Toast.makeText(a, R.string.failedSettingBlocCountValue, Toast.LENGTH_SHORT).show(); + } + int realBlocCount = (lastBlocCountIndex>=0)?useableBlocCount[lastBlocCountIndex]:-lastBlocCountIndex; + blocCountButton.setText(a.getResources().getString(R.string.blocPerFrage,Integer.toString(realBlocCount,10))); + + if(realBlocCount > 10){ + AlertDialog.Builder builder = new AlertDialog.Builder(a); + builder.setTitle(R.string.achtung); + builder.setIcon(android.R.drawable.ic_dialog_alert); + builder.setPositiveButton(android.R.string.ok,null); + TextView texte = new TextView(a); + texte.setText(R.string.blocCountABitHigh); + builder.setView(texte); + builder.show(); + } + } + }); + builder.setNegativeButton(android.R.string.no,new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + + + return true; + } + }); + blocCountButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.v("BCButton","simple click !"); + lastBlocCountIndex = (lastBlocCountIndex < 0)?DEFAULT_BLOC_VALUE:(lastBlocCountIndex + 1 >= useableBlocCount.length)?0:lastBlocCountIndex+1; + int realBlocCount = (lastBlocCountIndex>=0)?useableBlocCount[lastBlocCountIndex]:-lastBlocCountIndex; + + blocCountButton.setText(a.getResources().getString(R.string.blocPerFrage,Integer.toString(realBlocCount,10))); + } + }); + + frageCountBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + frageCountText.setText(a.getResources().getString(R.string.fragCount,progress)); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + }); + + View.OnLongClickListener frageCountLongListener = new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + Log.v("frageCount","looooooooooooong"); + AlertDialog.Builder builder = new AlertDialog.Builder(a); + builder.setTitle(R.string.frageCountAsk); + final EditText frageCountPrompt = new EditText(a); + frageCountPrompt.setInputType(InputType.TYPE_CLASS_NUMBER); + builder.setView(frageCountPrompt); + builder.setPositiveButton(android.R.string.ok,new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + try { + int n = Integer.parseInt(frageCountPrompt.getText().toString(), 10); + if(n>DEFAULT_FRAGE_COUNT_MAX) + frageCountBar.setMax(n); + else + frageCountBar.setMax(DEFAULT_FRAGE_COUNT_MAX); + frageCountBar.setProgress(n); + }catch(Exception e){ + Log.e("FrageCountDialog","Cannot read data",e); + Toast.makeText(a, R.string.failedSettingFrageCountValue, Toast.LENGTH_SHORT).show(); + } + } + }); + builder.setNegativeButton(android.R.string.no,new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + + + return false; + } + }; + frageCountText.setLongClickable(true); + frageCountBar.setLongClickable(true); + + frageCountBar.setOnLongClickListener(frageCountLongListener); + frageCountText.setOnLongClickListener(frageCountLongListener); + + frageCountBar.setProgress(preferences.getInt(LAST_FRAGE_COUNT,DEFAULT_FRAGE_COUNT)); + + return layout; + } + + @Override + public int getTypStringID() { + return R.string.blocQuiz; + } + + @Override + public int getIconDrawableID() { + return R.drawable.bloc_quiz_icon; + } + + @Override + public Data genDataFromConfigLayout(View v) { + SeekBar fC = v.findViewById(R.id.frageCount); + int realBlocCount = (lastBlocCountIndex>=0)?useableBlocCount[lastBlocCountIndex]:-lastBlocCountIndex; + BlocData bdata = new BlocData(realBlocCount,fC.getProgress()); + Log.v("BlocData","Selected : "+bdata.blockNumber+","+bdata.length); + return bdata; + } + } + +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/frageable/Frageable.java b/app/src/main/java/com/bernard/mcqinator/api/frageable/Frageable.java new file mode 100644 index 0000000..76ef68d --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/frageable/Frageable.java @@ -0,0 +1,42 @@ +package com.bernard.mcqinator.api.frageable; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.os.Parcelable; +import android.view.View; +import android.view.ViewGroup; + +import com.bernard.mcqinator.api.IdentifiedQuiz; +import com.bernard.mcqinator.api.fragen.Frage; +import com.bernard.mcqinator.view.activities.fragenActivities.FrageActivity; +import com.bernard.mcqinator.api.fragenStats.QuizStat; + +import java.util.List; + +/** + * @author samy + * @date on 05/05/17. + */ + +public interface Frageable { + + IdentifiedQuiz genFragen(S stat, FrData frageData, Context context, ProgressDialog wait); + S updateStats(S oldStats, List replies, ID identifier); + + //LATER remove parcelable + abstract class Data implements Parcelable { + public abstract Class> getFrageActivityClass(); + } + + //LATER move to view package + abstract class Configurator{ + public abstract int getTypStringID(); + + public abstract int getIconDrawableID(); + + public abstract View attachConfigLayout(Activity a,ViewGroup frame); + + public abstract Frageable.Data genDataFromConfigLayout(View v); + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/frageable/TextFrageable.java b/app/src/main/java/com/bernard/mcqinator/api/frageable/TextFrageable.java new file mode 100644 index 0000000..5cdb837 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/frageable/TextFrageable.java @@ -0,0 +1,172 @@ +package com.bernard.mcqinator.api.frageable; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.InputType; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.fragen.Frage; +import com.bernard.mcqinator.api.fragen.TextFrage; +import com.bernard.mcqinator.api.fragenStats.QuizStat; +import com.bernard.mcqinator.view.activities.MainActivity; +import com.bernard.mcqinator.view.activities.fragenActivities.FrageActivity; +import com.bernard.mcqinator.view.activities.fragenActivities.TextFrageActivity; + +/** + * @author mysaa + * @date on 3/13/18. + */ + +public interface TextFrageable extends Frageable{ + + String LAST_FRAGE_COUNT = "com.bernard.qcminator.LAST_TEXT_FRAGE_COUNT"; + int DEFAULT_FRAGE_COUNT = 10; + int DEFAULT_FRAGE_COUNT_MAX = 50; + + + class TextData extends Frageable.Data{ + public final int length; + + + public TextData(int length) { + this.length = length; + } + + public int getLength() { + return length; + } + + @Override + public Class> getFrageActivityClass() { + return TextFrageActivity.class; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(length); + } + + public static Parcelable.Creator CREATOR = new Parcelable.Creator(){ + @Override + public TextFrageable.TextData createFromParcel(Parcel source) { + return new TextFrageable.TextData(source.readInt()); + } + + @Override + public TextFrageable.TextData[] newArray(int size) { + return new TextFrageable.TextData[size]; + } + }; + } + + class TextConfigurator extends Frageable.Configurator{ + + @Override + public View attachConfigLayout(final Activity a, ViewGroup frame) { + final View layout = a.getLayoutInflater().inflate(R.layout.text_frage_options_layout,frame); + final TextView frageCountText = layout.findViewById(R.id.frageCountText); + final SeekBar frageCountBar = layout.findViewById(R.id.frageCount); + + frageCountBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + frageCountText.setText(a.getResources().getString(R.string.fragCount,progress)); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + }); + + View.OnLongClickListener frageCountLongListener = new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + Log.v("frageCount","looooooooooooong"); + AlertDialog.Builder builder = new AlertDialog.Builder(a); + builder.setTitle(R.string.frageCountAsk); + final EditText frageCountPrompt = new EditText(a); + frageCountPrompt.setInputType(InputType.TYPE_CLASS_NUMBER); + builder.setView(frageCountPrompt); + builder.setPositiveButton(android.R.string.ok,new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + try { + int n = Integer.parseInt(frageCountPrompt.getText().toString(), 10); + if(n>DEFAULT_FRAGE_COUNT_MAX) + frageCountBar.setMax(n); + else + frageCountBar.setMax(DEFAULT_FRAGE_COUNT_MAX); + frageCountBar.setProgress(n); + }catch(Exception e){ + Log.e("FrageCountDialog","Cannot read data",e); + Toast.makeText(a, R.string.failedSettingFrageCountValue, Toast.LENGTH_SHORT).show(); + } + } + }); + builder.setNegativeButton(android.R.string.no,new DialogInterface.OnClickListener(){ + @Override + public void onClick(DialogInterface dialog, int which) { + + } + }); + AlertDialog dialog = builder.create(); + dialog.show(); + + + return false; + } + }; + frageCountText.setLongClickable(true); + frageCountBar.setLongClickable(true); + + frageCountBar.setOnLongClickListener(frageCountLongListener); + frageCountText.setOnLongClickListener(frageCountLongListener); + + SharedPreferences preferences = a.getSharedPreferences(MainActivity.PREFERENCES_FILE_KEY,Context.MODE_PRIVATE); + + frageCountBar.setProgress(preferences.getInt(LAST_FRAGE_COUNT,DEFAULT_FRAGE_COUNT)); + + return layout; + } + + @Override + public int getTypStringID() { + return R.string.blocQuiz; + } + + @Override + public int getIconDrawableID() { + return R.drawable.bloc_quiz_icon; + } + + @Override + public Data genDataFromConfigLayout(View v) { + SeekBar fC = v.findViewById(R.id.frageCount); + return new TextData(fC.getProgress()); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/api/fragen/BlocFrage.java b/app/src/main/java/com/bernard/mcqinator/api/fragen/BlocFrage.java new file mode 100644 index 0000000..a1a7e6c --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/fragen/BlocFrage.java @@ -0,0 +1,110 @@ +package com.bernard.mcqinator.api.fragen; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * @author samy + * @date on 04/05/17. + */ + +public class BlocFrage extends Frage{ + private final String frage; + private final String richtigAntwort; + private final String[] falshAntworte; + + public BlocFrage(String frage, String richtigAntwort, String[] falshAntworte) { + this.frage = frage; + this.richtigAntwort = richtigAntwort; + this.falshAntworte = falshAntworte; + } + + public String getFrage() { + return frage; + } + + public String getRichtigAntwort() { + return richtigAntwort; + } + + public String[] getFalshAntworte() { + return falshAntworte; + } + + public static Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public BlocFrage createFromParcel(Parcel source) { + String frage = source.readString(); + String richtigAntwort = source.readString(); + String[] falshAntworte = new String[source.readInt()]; + source.readStringArray(falshAntworte); + return new BlocFrage(frage,richtigAntwort,falshAntworte); + } + + @Override + public BlocFrage[] newArray(int size) { + return new BlocFrage[size]; + } + + }; + + @Override + public int describeContents() {return 0;} + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(frage); + dest.writeString(richtigAntwort); + dest.writeInt(falshAntworte.length); + dest.writeStringArray(falshAntworte); + } + + @Override + public Class getReplyClass() { + return Reply.class; + } + + @Override + public String toString() { + return "BlocFrage{" + + "frage='" + frage + '\'' + + ", richtigAntwort='" + richtigAntwort + '\'' + + ", falshAntworte=" + Arrays.toString(falshAntworte) + + "} " + super.toString(); + } + + public static class Reply extends Frage.Reply{ + final String answered; + + public Reply(String answered) { + this.answered = answered; + } + + public String getAnswered() { + return answered; + } + + public static Parcelable.Creator CREATOR = new BlocFrage.Creator() { + public BlocFrage.Reply createFromParcel(Parcel source) { + return new BlocFrage.Reply(source.readString()); + } + + @Override + public Reply[] newArray(int size) { + return new Reply[size]; + } + }; + + + @Override + public int describeContents() {return 0;} + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(answered); + } + + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/fragen/Frage.java b/app/src/main/java/com/bernard/mcqinator/api/fragen/Frage.java new file mode 100644 index 0000000..5629667 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/fragen/Frage.java @@ -0,0 +1,38 @@ +package com.bernard.mcqinator.api.fragen; + +import android.os.Parcelable; + +import com.bernard.mcqinator.view.activities.fragenActivities.BlocFrageActivity; +import com.bernard.mcqinator.view.activities.fragenActivities.FrageActivity; + +/** + * @author samy + * @date on 04/05/17. + */ + +public abstract class Frage implements Parcelable { + + public enum Type { + BLOC(BlocFrageActivity.class); + final Class frageActivity; + + Type(Class frageActivity) { + this.frageActivity = frageActivity; + } + + public Class getFrageActivity() { + return frageActivity; + } + } + + public abstract Class getReplyClass(); + + public abstract static class Reply implements Parcelable{ + + } + + public abstract static class Identifier { + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/api/fragen/TextFrage.java b/app/src/main/java/com/bernard/mcqinator/api/fragen/TextFrage.java new file mode 100644 index 0000000..b859658 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/fragen/TextFrage.java @@ -0,0 +1,97 @@ +package com.bernard.mcqinator.api.fragen; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author mysaa + * @date on 3/13/18. + */ + +public class TextFrage extends Frage { + private final String frage; + private final String richtigAntwort; + + public TextFrage(String frage, String richtigAntwort) { + this.frage = frage; + this.richtigAntwort = richtigAntwort; + } + + public String getFrage() { + return frage; + } + + public String getRichtigAntwort() { + return richtigAntwort; + } + + public static Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public TextFrage createFromParcel(Parcel source) { + String frage = source.readString(); + String richtigAntwort = source.readString(); + return new TextFrage(frage,richtigAntwort); + } + + @Override + public TextFrage[] newArray(int size) { + return new TextFrage[size]; + } + + }; + + @Override + public int describeContents() {return 0;} + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(frage); + dest.writeString(richtigAntwort); + } + + @Override + public Class getReplyClass() { + return TextFrage.Reply.class; + } + + @Override + public String toString() { + return "BlocFrage{" + + "frage='" + frage + '\'' + + ", richtigAntwort='" + richtigAntwort + '\'' + + "} " + super.toString(); + } + + public static class Reply extends Frage.Reply{ + final String answered; + + public Reply(String answered) { + this.answered = answered; + } + + public String getAnswered() { + return answered; + } + + public static Parcelable.Creator CREATOR = new TextFrage.Creator() { + public TextFrage.Reply createFromParcel(Parcel source) { + return new TextFrage.Reply(source.readString()); + } + + @Override + public TextFrage.Reply[] newArray(int size) { + return new TextFrage.Reply[size]; + } + }; + + + @Override + public int describeContents() {return 0;} + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(answered); + } + + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/fragenStats/FormQuizStat.java b/app/src/main/java/com/bernard/mcqinator/api/fragenStats/FormQuizStat.java new file mode 100644 index 0000000..50b8182 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/fragenStats/FormQuizStat.java @@ -0,0 +1,140 @@ +package com.bernard.mcqinator.api.fragenStats; + +import com.bernard.mcqinator.api.quiz.FormQuiz; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +/** + * @author samy + * @date on 26/05/17. + */ + +public class FormQuizStat extends QuizStat { + /* TODO pass to formquiz */ + protected volatile int failSum; + protected volatile int[] failSums; + protected volatile int[][] failsSum; + protected volatile int[][][] failsSums; + protected volatile int[][][][] fails; + + + @Override + public void genDefault(FormQuiz q) { + FormQuiz.Item[] items = q.getItems(); + fails = new int[items.length][][][]; + for (int i = 0; i < items.length; i++) { + fails[i] = new int[items[i].getForms().length][][]; + for (int j = 0; j < items[i].getForms().length; j++) { + fails[i][j] = new int[items[i].getForms().length - 1][]; + for (int k = 0; k < items[i].getForms().length - 1; k++) { + int[] current = new int[items[i].getForms()[j].getFalshe().length]; + Arrays.fill(current,1); + fails[i][j][k] = current; + } + } + } + genVolatiles(); + } + + @Override + public void read(DataInputStream dis) throws IOException { + fails = new int[dis.readInt()][][][]; + for (int i = 0; i < fails.length; i++) { + fails[i] = new int[dis.readInt()][][]; + for (int j = 0; j < fails[i].length; j++) { + fails[i][j] = new int[dis.readInt()][]; + for (int k = 0; k < fails[i][j].length; k++) { + fails[i][j][k] = new int[dis.readInt()]; + for (int l = 0; l < fails[i][j][k].length; l++) + fails[i][j][k][l] = dis.readInt(); + } + } + } + genVolatiles(); + } + + @Override + public void write(DataOutputStream dos) throws IOException { + dos.writeInt(fails.length); + for (int[][][] fails1:fails) { + dos.writeInt(fails1.length); + for (int[][] fails2:fails1) { + dos.writeInt(fails2.length); + for (int[] fails3:fails2) { + dos.writeInt(fails3.length); + for (int fails4 : fails3) + dos.writeInt(fails4); + } + } + } + } + + public void genVolatiles(){ + failSum = 0; + failSums = new int[fails.length]; + failsSum = new int[fails.length][]; + failsSums = new int[fails.length][][]; + for (int i = 0; i < fails.length; i++) { + failSums[i] = 0; + failsSum[i] = new int[fails[i].length]; + failsSums[i] = new int[fails[i].length][]; + for (int j = 0; j < fails[i].length; j++) { + failsSum[i][j] = 0; + failsSums[i][j] = new int[fails[i][j].length]; + for (int k = 0; k < fails[i][j].length; k++) { + failsSums[i][j][k] = 0; + for (int l = 0; l < fails[i][j][k].length; l++) + failsSums[i][j][k] += fails[i][j][k][l]; + failsSum[i][j] += failsSums[i][j][k]; + } + failSums[i] += failsSum[i][j]; + } + failSum += failSums[i]; + } + } + + + public int failSum(){ + return failSum; + } + + public int failSum(int item){ + return failSums[item]; + } + + public int failSum(int item,int form){ + return failsSum[item][form]; + } + + public int failSum(int item,int form,int frageForm){ + return failsSums[item][form][frageForm]; + } + + public int fails(int item,int form,int frageForm,int falshe){ + return fails[item][form][frageForm][falshe]; + } + + + public int[] failSums(){ + return failSums; + } + + public int[] failSums(int item){ + return failsSum[item]; + } + + public int[] failSums(int item,int form){ + return failsSums[item][form]; + } + + public int[] failSums(int item,int form,int falsheForm){ + return fails[item][form][falsheForm]; + } + + public void addAFail(int item, int form,int frageForm, int falshe) { + this.fails[item][form][frageForm][falshe]++; + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/fragenStats/QuizStat.java b/app/src/main/java/com/bernard/mcqinator/api/fragenStats/QuizStat.java new file mode 100644 index 0000000..9504ba0 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/fragenStats/QuizStat.java @@ -0,0 +1,50 @@ +package com.bernard.mcqinator.api.fragenStats; + +import android.util.Log; + +import com.bernard.mcqinator.api.quiz.Quiz; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.regex.Pattern; + +/** + * @author Mysaa + * @date on 04/05/17. + */ + +public abstract class QuizStat { + + private static final Pattern QUIZ_FILE_PATTERN = Pattern.compile("^stats_([0-9]+).bernard$"); + + public static QuizStat createStat(Class statClass,Quiz q){ + try { + QuizStat stat = statClass.newInstance(); + checkedGenDefault(stat,q); + return stat; + } catch (InstantiationException e) { + Log.e("StatCreator","The class "+statClass.getName()+" is not instatiable ... try to add a 'no arg' constructor",e); + } catch (IllegalAccessException e) { + Log.e("StatCreator","The class "+statClass.getName()+" is not reachable ... try to make it public",e); + } + return null; + } + + @SuppressWarnings("unchecked") + private static void checkedGenDefault(QuizStat s,Quiz q){ + s.genDefault(q); + } + + public abstract void genDefault(Q q); + + + //////////// Getters / Setters //////////// + + //////////// File Methods //////////// + + public abstract void read(DataInputStream dis) throws IOException; + + public abstract void write(DataOutputStream dos) throws IOException; + +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/quiz/DEVerbenQuiz.java b/app/src/main/java/com/bernard/mcqinator/api/quiz/DEVerbenQuiz.java new file mode 100644 index 0000000..8297200 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/quiz/DEVerbenQuiz.java @@ -0,0 +1,109 @@ +package com.bernard.mcqinator.api.quiz; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import com.bernard.mcqinator.R; + +/** + * @author samy + * @date on 17/04/17. + */ + +public class DEVerbenQuiz extends FormQuiz{ + + private static final int[] formNamesIDs = {R.string.infinitif,R.string.prasens2,R.string.prasens3,R.string.prateritum2,R.string.prateritum3,R.string.partizipPerfekt}; + + public static final int AUX_HABEN = 0b0,AUX_SEIN = 0b1; + + + @Override + public String getBlocFrageText(int itemID, int formID, int fformID, BlocData frageData, Context context) { + return context.getResources().getString(R.string.wasIstDasVon,context.getResources().getString(formNamesIDs[getItems()[itemID].getForms()[formID].getFormID()]),getItems()[itemID].getForms()[fformID].getRichtig(),context.getResources().getString(formNamesIDs[getItems()[itemID].getForms()[fformID].getFormID()])); + } + + @Override + public int getEditActivityLayoutID() { + return R.layout.activity_edit_quiz_deverben; + } + + @Override + public int getEditItemActivityFormLayoutID() { + return R.layout.edit_form_quiz_item_form; + } + + @Override + public int getEditItemActivityFalseLayoutID() { + return R.layout.edit_form_quiz_item_false; + } + + @Override + public int getEditItemActivityLayoutID() { + return R.layout.edit_form_quiz_item; + } + + @Override + public int getFormCount() { + return formNamesIDs.length; + } + + @Override + protected View getEditQuizItemView(Item item, int position, View convertView, ViewGroup parent,Context c) { + View group = (convertView == null)?LayoutInflater.from(c).inflate(R.layout.verbe_row_layout,parent,false):(ViewGroup)convertView; + ((TextView)group.findViewById(R.id.verbeInfinitifText)).setText(item.getForms()[0].getRichtig()); + ((TextView)group.findViewById(R.id.verbeOthersText)).setText(item.getForms()[1].getRichtig()+item.getForms()[3].getRichtig()+item.getForms()[5].getRichtig()); + int ram = 0; + for (Item.Form f : item.getForms()) + ram+=f.getFalshe().length + 1; + ((TextView)group.findViewById(R.id.verbeInfinitifText)).setText(c.getResources().getQuantityString(R.plurals.worteCount,ram,ram)); + + return group; + } + + @Override + public int getFormNameStringId(int pos) { + return formNamesIDs[pos]; + } + + public class VerbeAdapter extends BaseAdapter{ + final Context ctx; + + public VerbeAdapter(Context ctx) { + this.ctx = ctx; + } + + @Override + public int getCount() { + return getItems().length; + } + + @Override + public Item getItem(int position) { + return getItems()[position]; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View layout = (convertView != null)?convertView: LayoutInflater.from(ctx).inflate(R.layout.verbe_row_layout,parent,false); + TextView infinitif = layout.findViewById(R.id.verbeInfinitifText); + TextView others = layout.findViewById(R.id.verbeOthersText); + TextView worteCount = layout.findViewById(R.id.verbeWorteCountText); + + Item i = this.getItem(position); + infinitif.setText(i.getForms()[0].getRichtig()); + others.setText(i.getForms()[3].getRichtig() + "," + i.getForms()[5].getRichtig() + "," + i.getForms()[6].getRichtig()); + int wC = i.getForms()[0].getFalshe().length + i.getForms()[1].getFalshe().length + i.getForms()[2].getFalshe().length + i.getForms()[3].getFalshe().length + i.getForms()[4].getFalshe().length + i.getForms()[5].getFalshe().length + i.getForms()[6].getFalshe().length; + worteCount.setText(ctx.getResources().getQuantityString(R.plurals.worteCount,wC,wC)); + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/api/quiz/FormQuiz.java b/app/src/main/java/com/bernard/mcqinator/api/quiz/FormQuiz.java new file mode 100644 index 0000000..740dd9e --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/quiz/FormQuiz.java @@ -0,0 +1,456 @@ +package com.bernard.mcqinator.api.quiz; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import com.bernard.mcqinator.api.IdentifiedQuiz; +import com.bernard.mcqinator.api.frageable.BlocFrageable; +import com.bernard.mcqinator.api.fragen.BlocFrage; +import com.bernard.mcqinator.api.fragen.Frage; +import com.bernard.mcqinator.api.fragenStats.FormQuizStat; +import com.bernard.mcqinator.inutil.ArrayInutil; +import com.bernard.mcqinator.inutil.NumberInutil; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import static com.bernard.mcqinator.inutil.ArrayInutil.asize; +import static com.bernard.mcqinator.inutil.ArrayInutil.count; +import static com.bernard.mcqinator.inutil.ArrayInutil.range; +import static com.bernard.mcqinator.inutil.ArrayInutil.toObjectArray; + +/** + * @author Mysaa + * @date on 28/05/17. + */ + +public abstract class FormQuiz extends Quiz implements BlocFrageable { + + private FormQuiz.Item[] items; + private String[] idS; + + //TODO make forms no-declareable (withs forms IDs) + + //////////// BlocFrage //////////// + @Override + public IdentifiedQuiz genFragen(FormQuizStat stat, BlocData frageData, Context context, ProgressDialog wait) { + + // Initialisation des variables + Random r = new Random(); + + // Correction des potentielles erreurs du quiz + if(items.length <= 0) + throw new IllegalStateException("There is no frage in this quiz !");//TODO implement special error, that is toasted to the user + + // Selection des items + List itemsPos = asize(range(0,items.length),frageData.length,toObjectArray(stat.failSums()),stat.failSum(),r); + Map itemsPosAndCount = count(itemsPos); + + //Selection de la form à trouver + + Map formsPosAndCount = new HashMap<>(); + + for(Map.Entry e : itemsPosAndCount.entrySet()){ + Integer[][] formsIDs = new Integer[items[e.getKey()].forms.length][2]; + for (int i = 0; i < formsIDs.length; i++) + formsIDs[i] = new Integer[]{e.getKey(),i}; + Map formPosAndCount = count(asize(formsIDs,e.getValue(),toObjectArray(stat.failSums(e.getKey())),stat.failSum(e.getKey()),r)); + formsPosAndCount.putAll(formPosAndCount); + } + + // Selection de la form question + Map fformsPosAndCount = new HashMap<>(); + + for(Map.Entry e : formsPosAndCount.entrySet()){ + int itemID = e.getKey()[0]; + int formID = e.getKey()[1]; + List fformsIDs = new ArrayList<>(); + for (int i = 0; i < items[itemID].forms.length - 1; i++) + fformsIDs.add(new Integer[]{itemID,formID, i}); + Log.v("R","Rutabaga" + itemID); + Map fformPosAndCount = count(asize(fformsIDs,e.getValue(),toObjectArray(stat.failSums(itemID,formID)),stat.failSum(itemID,formID),r)); + Log.v("R","Salsifi"); + fformsPosAndCount.putAll(fformPosAndCount); + } + + // Selection des mauvaises réponces + List fragen = new ArrayList<>(); + + + for(Map.Entry e : fformsPosAndCount.entrySet()){ + int itemID = e.getKey()[0]; + int formID = e.getKey()[1]; + int fformID = e.getKey()[2]; + String frageText = getBlocFrageText(itemID,formID, NumberInutil.exclude(fformID,formID),frageData,context); + Item.Form form = items[itemID].getForms()[formID]; + String richtigAntwort = form.getRichtig(); + List falsheIDs = new ArrayList<>(); + for (int i = 0; i < items[itemID].forms[formID].getFalshe().length; i++) + falsheIDs.add(new Integer[]{itemID,formID,fformID,i}); + List falshPoz = asize(falsheIDs,e.getValue() * (frageData.blockNumber - 1),toObjectArray(stat.failSums(itemID,formID,fformID)),stat.failSum(itemID,formID,fformID),r); + for (int i = 0; i < falshPoz.size(); i+=(frageData.blockNumber - 1)) { + List falsheBloc = falshPoz.subList(i,i+(frageData.blockNumber - 1)); + String[] falsheBlocArray = new String[falsheBloc.size()]; + for (int j = 0; j < falsheBlocArray.length; j++) + falsheBlocArray[j] = form.getFalshe()[falsheBloc.get(j)[3]]; + + fragen.add(new BlocFrage(frageText,richtigAntwort,falsheBlocArray)); + } + + } + + List idsFragen = new ArrayList<>(fformsPosAndCount.keySet()); + List poss = Arrays.asList(ArrayInutil.range(0,idsFragen.size())); + Collections.shuffle(poss,r); + + Integer[][] shuffledIdsFragen = new Integer[idsFragen.size()][]; + BlocFrage[] shuffledFragenArray = new BlocFrage[fragen.size()]; + + for (int i = 0; i < poss.size(); i++) { + shuffledIdsFragen[i] = idsFragen.get(poss.get(i)); + shuffledFragenArray[i] = fragen.get(poss.get(i)); + } + Log.v("BlocFrageGenerator","Done !"); + + return new IdentifiedQuiz<>(shuffledFragenArray,new Identifier(ArrayInutil.toPrimitiveArray(shuffledIdsFragen))); + } + + public abstract String getBlocFrageText(int itemID, int formID, int fformID, BlocData frageData, Context context); + + @Override + public FormQuizStat updateStats(FormQuizStat oldStats, List replies,FormQuiz.Identifier identifier) { + if(replies.size() != identifier.formIndexes.length)throw new IllegalArgumentException("The identifier dont match with the number of replies recieved"); + for (int i = 0; i < replies.size(); i++) { + int itemID = identifier.formIndexes[i][0]; + int formID = identifier.formIndexes[i][1]; + int fformID = identifier.formIndexes[i][2]; + String answered = replies.get(i).getAnswered(); + if(items[itemID].getForms()[formID].getRichtig().equals(answered)) + continue; + int falsheAntwortID = ArrayInutil.indexOf(items[itemID].getForms()[formID].getFalshe(),answered); + oldStats.addAFail(itemID,formID,fformID,falsheAntwortID); + } + return oldStats; + } + + @Override + public void genDefault() { + items = new FormQuiz.Item[0]; + } + + + //////////// Getters / Setters //////////// + + //TODO set setters for editactivities + + public void addItem(Item i){ + Item[] litem = new Item[items.length+1]; + System.arraycopy(items,0,litem,0,items.length); + litem[litem.length-1] = i; + items = litem; + } + + public void setItem(int pos,Item i){ + items[pos] = i; + } + + public FormQuiz.Item[] getItems() { + return items; + } + + public FormQuiz.Item getItem(int pos) { + return items[pos]; + } + + + //////////// Customization methods //////////// + + //TODO add more abstract methods for a full customisation + + public abstract int getEditActivityLayoutID(); + + public abstract int getEditItemActivityFormLayoutID(); + + public abstract int getEditItemActivityFalseLayoutID(); + + public abstract int getEditItemActivityLayoutID(); + + protected abstract View getEditQuizItemView(Item item, int position, View convertView, ViewGroup parent, Context c); + + public abstract int getFormNameStringId(int pos); + + public abstract int getFormCount(); + + + //////////// File Methods //////////// + + public void write(DataOutputStream dos) throws IOException{ + dos.writeInt(items.length); + for (Item i : items) + i.write(dos); + } + + public void read(DataInputStream dis) throws IOException{ + items = new Item[dis.readInt()]; + for (int i = 0; i < items.length; i++) { + items[i] = new Item(); + items[i].read(dis); + } + } + + + //////////// Internal Classes //////////// + + //TODO implement all + //TODO remove parcelable and serialiable if not used + public static class Item implements Parcelable,Serializable { + + Form[] forms; + + public Item(Form[] forms) { + this.forms = forms; + } + + public Item() {} // For serialization + + //////////// Getters / Setters //////////// + + public Form[] getForms() { + return forms; + } + + public void setForms(Form[] forms) { + this.forms = forms; + } + + //////////// Parcelable //////////// + + protected Item(Parcel in) { + forms = in.createTypedArray(Form.CREATOR); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Item createFromParcel(Parcel in) { + return new Item(in); + } + + @Override + public Item[] newArray(int size) { + return new Item[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeTypedArray(forms,0); + } + + //////////// File Methods //////////// + + public void write(DataOutputStream dos) throws IOException{ + dos.writeInt(forms.length); + for (Form f : forms) + f.write(dos); + } + + public void read(DataInputStream dis) throws IOException{ + forms = new Form[dis.readInt()]; + for (int i = 0; i < forms.length; i++) { + forms[i] = new Form(); + forms[i].read(dis); + } + } + + //////////// Internal Classes //////////// + + public static class Form implements Parcelable,Serializable { + + String richtig; + String[] falshe; + int formID; + + public Form(int formID,String richtig, String... falshe) { + this.richtig = richtig; + this.falshe = falshe; + this.formID = formID; + } + + protected Form(Parcel in) { + formID = in.readInt(); + richtig = in.readString(); + falshe = in.createStringArray(); + } + + public Form() {} //For serialization + + //////////// Getters / Setters //////////// + + public String getRichtig() { + return richtig; + } + + public String[] getFalshe() { + return falshe; + } + + public int getFormID() { + return formID; + } + + /////////// File Methods //////////// + + public void write(DataOutputStream dos) throws IOException{ + dos.writeInt(formID); + if(richtig == null) { + dos.writeInt(0); + }else{ + dos.writeInt(richtig.getBytes().length); + dos.write(richtig.getBytes()); + } + + dos.writeInt(falshe.length); + for(String f : falshe){ + if(f == null){ + dos.writeInt(0); + }else { + dos.writeInt(f.getBytes().length); + dos.write(f.getBytes()); + } + } + } + + public void read(DataInputStream dis) throws IOException{ + formID = dis.readInt(); + byte[] richtigBytes = new byte[dis.readInt()]; + int read = dis.read(richtigBytes); + if(read != richtigBytes.length)throw new IOException("Fichier invalide ... la taille annoncée du string richtig n'est pas satisfaite "); + richtig = new String(richtigBytes); + falshe = new String[dis.readInt()]; + for (int i = 0; i < falshe.length; i++) { + byte[] falshBytes = new byte[dis.readInt()]; + read = dis.read(falshBytes); + if(read != falshBytes.length)throw new IOException("Fichier invalide ... la taille annoncée du string falche n°"+i+" n'est pas satisfaite "); + falshe[i] = new String(falshBytes); + } + } + + //////////// Parcelable //////////// + + public static final Creator
CREATOR = new Creator() { + @Override + public Form createFromParcel(Parcel in) { + return new Form(in); + } + + @Override + public Form[] newArray(int size) { + return new Form[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(formID); + dest.writeString(richtig); + dest.writeStringArray(falshe); + } + + /////////// Misc //////////// + + @Override + public String toString() { + return "Form{" + + "richtig='" + richtig + '\'' + + ", falshe=" + Arrays.toString(falshe) + + ", formID=" + formID + + '}'; + } + } + + //////////// Misc //////////// + + @Override + public String toString() { + return "Item{" + + "forms=" + Arrays.toString(forms) + + '}'; + } + + } + + public static class Identifier extends Frage.Identifier{ + /* + Array of type {[ItemPos,FormPos,FformPos],[ItemPos,FormPos,FformPos],...} + */ + final int[][] formIndexes; + + + public Identifier(int[][] formIndexes) { + this.formIndexes = formIndexes; + } + } + + //LATER move + public class ItemAdapter extends BaseAdapter{ + + final Context c; + + public ItemAdapter(Context c) { + this.c = c; + } + + @Override + public int getCount() { + return items.length; + } + + @Override + public Item getItem(int position) { + return items[position]; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return FormQuiz.this.getEditQuizItemView(items[position], position, convertView, parent,c); + } + } + + + //////////// Misc //////////// + + @Override + public String toString() { + return "DEVerbenQuiz (" + Arrays.deepToString(items) + ")"; + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/api/quiz/Quiz.java b/app/src/main/java/com/bernard/mcqinator/api/quiz/Quiz.java new file mode 100644 index 0000000..cec5780 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/api/quiz/Quiz.java @@ -0,0 +1,22 @@ +package com.bernard.mcqinator.api.quiz; + +import com.bernard.mcqinator.api.fragenStats.QuizStat; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; + +/** + * Created by samy on 14/04/17. + * @author Mysaa + */ + +public abstract class Quiz implements Serializable { + + public abstract void genDefault(); + + public abstract void write(DataOutputStream fos) throws IOException; + + public abstract void read(DataInputStream fin) throws IOException; +} diff --git a/app/src/main/java/com/bernard/mcqinator/controller/FileManager.java b/app/src/main/java/com/bernard/mcqinator/controller/FileManager.java new file mode 100644 index 0000000..b997987 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/controller/FileManager.java @@ -0,0 +1,301 @@ +package com.bernard.mcqinator.controller; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.bernard.mcqinator.api.QuizMetadata; +import com.bernard.mcqinator.api.fragenStats.QuizStat; +import com.bernard.mcqinator.api.quiz.Quiz; +import com.bernard.mcqinator.inutil.NumberInutil; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Random; + +/** + * @author Mysaa + * @date on 22/07/17. + */ + +public final class FileManager { + + private static final Random fileNamesRandom = new Random(); + + + //////////// Consts //////////// + + public static final byte[] appBernardKey = {0b00100010,0b01001001}; + public static final byte[] appBernardKeyStat = {0b00100011,0b01001000}; + public static final byte[] bernardSignature = {0b00011001,0b01101101}; + + + //////////// QuizStat //////////// + + public static String statFilename(long fileID){ + return "stat_"+ NumberInutil.toUnsignedString(fileID)+".bernard"; + } + + public static String statFilename(QuizMetadata quiz){ + return statFilename(quiz.getFileID()); + } + + ////// Save ////// + + public static void saveStat(QuizMetadata quiz,QuizStat stat,Context ctx){ + try { + ctx.deleteFile(statFilename(quiz)); + saveStatAsFile(stat,quiz,new DataOutputStream(ctx.openFileOutput(statFilename(quiz),Context.MODE_PRIVATE))); + } catch (IOException e) { + Log.e("FileManager","Can't save the quiz stat " + stat,e); + } + } + + public static void saveStatAsFile(QuizStat stat,QuizMetadata meta,DataOutputStream dos,boolean closeDatautputStream) throws IOException { + //LATER implement bernard API + //Writing Bernard signature + dos.write(FileManager.bernardSignature); + + //Writing Bernard key + dos.write(FileManager.appBernardKeyStat); + + //Writing fileType + dos.writeInt(meta.getType().getId()); + + //Writing quiz + stat.write(dos); + + if(closeDatautputStream)dos.close(); + } + + public static void saveStatAsFile(QuizStat stat,QuizMetadata meta,DataOutputStream dos) throws IOException { + saveStatAsFile(stat,meta,dos,true); + } + + ////// Get ////// + + public static QuizStat getStat(QuizMetadata quiz, Context ctx){ + try { + return getFromFile(new DataInputStream(ctx.openFileInput(statFilename(quiz.getFileID())))); + }catch (IOException e) { + Log.e("FileManager","Cannot read QuizStat of quiz " + quiz,e); + } + return null; + } + + public static QuizStat getFromFile(DataInputStream din) throws IOException { + + //LATER implement bernard API + int readBytes; + + //Assert bernard signature + byte[] bernardSignature = new byte[2]; + readBytes = din.read(bernardSignature); + if(readBytes != 2 || bernardSignature[0] != FileManager.bernardSignature[0] || bernardSignature[1] != FileManager.bernardSignature[1]){ + din.close(); + throw new IOException("Corrupted quizStat : wrong bernard signature"); + } + + + byte[] bernardType = new byte[2]; + readBytes = din.read(bernardType); + if(readBytes != 2 || bernardType[0] != FileManager.appBernardKeyStat[0] || bernardType[1] != FileManager.appBernardKeyStat[1]) { + din.close(); + throw new IOException("Corrupted quizStat : wrong QuizStat key"); + } + + int quizTypeNum = din.readInt(); + QuizMetadata.Type type = QuizMetadata.Type.getTypeFromID(quizTypeNum); + if(type == null){ + din.close(); + throw new IOException("Corrupted quizStat : unknown QuizType ID"); + } + + QuizStat actual; + try{ + actual = type.getQuizStatClass().newInstance(); + actual.read(din); + Log.v("FileManager","QuizStat successfully read !"); + }catch(Exception e) { + din.close(); + throw new IOException("Corrupted quizStat : unreadable content", e); + } + din.close(); + return actual; + } + + + //////////// Quiz //////////// + + private static String quizFilename(long fileID){ + return "quiz_"+ NumberInutil.toUnsignedString(fileID)+".bernard"; + } + + private static String quizFilename(QuizMetadata quiz){ + return quizFilename(quiz.getFileID()); + } + + ////// Save ////// + + public static void saveQuiz(QuizMetadata meta,Quiz q,Context ctx){ + try { + ctx.deleteFile(quizFilename(meta)); + saveQuizAsFile(new DataOutputStream(ctx.openFileOutput(quizFilename(meta),Context.MODE_PRIVATE)), meta, q); + } catch (IOException e) { + Log.e("FileManager","Cant save the quiz " + q + "at" + meta,e); + } + } + + private static void saveQuizAsFile(DataOutputStream dos,QuizMetadata meta, Quiz q,boolean justPayload,boolean closeDataOutputStream) throws IOException { + + if(!justPayload) { + //Writing Bernard signature + dos.write(FileManager.bernardSignature); + + //Writing Bernard key + dos.write(FileManager.appBernardKey); + } + + //Writing quizType key + dos.writeInt(meta.getType().getId()); + + //Writing quiz + q.write(dos); + + if(closeDataOutputStream)dos.close(); + } + + private static void saveQuizAsFile(DataOutputStream dos,QuizMetadata meta, Quiz q) throws IOException { + saveQuizAsFile(dos,meta,q,false,true); + } + + ////// Get ////// + + @Nullable + private static Quiz getQuizFromFile(DataInputStream dis,boolean justPayload) throws IOException { + + int readBytes; + + if(!justPayload) { + + //Assert bernard signature + byte[] bernardSignature = new byte[2]; + readBytes = dis.read(bernardSignature); + if(readBytes != 2 || bernardSignature[0] != FileManager.bernardSignature[0] || bernardSignature[1] != FileManager.bernardSignature[1]){ + dis.close(); + throw new IOException("Corrupted quiz : wrong bernard signature"); + } + + + byte[] bernardType = new byte[2]; + readBytes = dis.read(bernardType); + if(readBytes != 2 || bernardType[0] != FileManager.appBernardKey[0] || bernardType[1] != FileManager.appBernardKey[1]) { + dis.close(); + throw new IOException("Corrupted quiz : wrong Quiz key"); + } + } + int quizTypeNum = dis.readInt(); + QuizMetadata.Type type = QuizMetadata.Type.getTypeFromID(quizTypeNum); + if(type == null){ + dis.close(); + throw new IOException("Unknown quiz type"); + } + + Quiz actual; + try{ + actual = type.getQuizClass().newInstance(); + actual.read(dis); + Log.v("FileManager","Quiz successfully read"); + }catch(Exception e){ + dis.close(); + throw new IOException("Corrupted quiz : wrong content",e); + } + dis.close(); + return actual; + } + + private static Quiz getQuizFromFile(DataInputStream dis) throws IOException { + return getQuizFromFile(dis,false); + } + + public static Quiz getQuiz(QuizMetadata meta,Context ctx) throws IOException { + try { + return getQuizFromFile(new DataInputStream(ctx.openFileInput(quizFilename(meta.getFileID())))); + }catch (IOException e) { + Log.e("FileManager","Cannot read Quiz with meta " + meta,e); + } + return null; + } + + ////// Import / Export ////// + + public static void exportQuizToFile(QuizMetadata meta,Context ctx,File f){ + try { + //LATER implement Bernard API + DataOutputStream dos = new DataOutputStream(new FileOutputStream(f)); + long temp = meta.getFileID(); + meta.setFileID(-1); + meta.write(dos); + meta.setFileID(temp); + saveQuizAsFile(dos,meta,getQuiz(meta,ctx),true,true);//Do the dos.close() + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Nullable + public static QuizMetadata importQuizFromFile(Context ctx,File f) throws IOException { + DataInputStream dis = new DataInputStream(new FileInputStream(f)); + + int readBytes; + + byte[] bernardSignature = new byte[2]; + readBytes = dis.read(bernardSignature); + if(readBytes != 2 || bernardSignature[0] != FileManager.bernardSignature[0] || bernardSignature[1] != FileManager.bernardSignature[1]){ + dis.close(); + throw new IOException("Corrupted quiz : wrong bernard sign"); + } + + byte[] bernardType = new byte[2]; + readBytes = dis.read(bernardType); + if(readBytes != 2 || bernardType[0] != FileManager.appBernardKey[0] || bernardType[1] != FileManager.appBernardKey[1]) { + dis.close(); + throw new IOException("Corrupted quiz : wrong app sign"); + } + + //TODO add special key to QUIZ_WITH_METADATA + + QuizMetadata toImport = new QuizMetadata(); + toImport.read(dis); + + Quiz q = getQuizFromFile(dis,true); + + QuizMetadata newMetadata = QuizMetadataManager.createQuiz(ctx,toImport.getName(),toImport.getAuthor(),toImport.getType(),toImport.isEditable()); + saveQuiz(newMetadata,q,ctx); + return newMetadata; + } + + + //////////// Global //////////// + + public static long getAvalivableFileID(Context ctx){ + long fileID; + do{ + fileID = fileNamesRandom.nextLong(); + }while(new File(ctx.getFilesDir(),quizFilename(fileID)).exists() || new File(ctx.getFilesDir(),statFilename(fileID)).exists()); + return fileID; + } + + public static boolean delete(QuizMetadata m,Context c) { + return c.deleteFile(quizFilename(m)) && !c.deleteFile(statFilename(m)) && QuizMetadataManager.delete(m); + } + + + + + +} diff --git a/app/src/main/java/com/bernard/mcqinator/controller/QuizMetadataManager.java b/app/src/main/java/com/bernard/mcqinator/controller/QuizMetadataManager.java new file mode 100644 index 0000000..00caa07 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/controller/QuizMetadataManager.java @@ -0,0 +1,187 @@ +package com.bernard.mcqinator.controller; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.bernard.mcqinator.api.QuizMetadata; +import com.bernard.mcqinator.api.quiz.Quiz; +import com.bernard.mcqinator.inutil.ArrayInutil; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author samy + * @date on 23/07/17. + */ + +public final class QuizMetadataManager { + + private static List savedQuizs; + + + private static final String SAVED_METADATA_FILENAME = "savedQuizz.bernard"; + + public static void saveSavedQuizList(Context ctx){ + DataOutputStream dos = null; + try { + //LATER implement Bernard API + dos = new DataOutputStream(ctx.openFileOutput(SAVED_METADATA_FILENAME, Context.MODE_PRIVATE)); + dos.write(FileManager.bernardSignature); + dos.write(FileManager.appBernardKey); + dos.writeInt(-1);// Metadata file identifier + dos.writeInt(savedQuizs.size()); + + for (QuizMetadata m : savedQuizs) + m.write(dos); + + Log.v("QuizMetadataManager","Quiz metadatas saved !"); + } catch (IOException e) { + Log.e("QuizMetadataManager","Cannot save quiz metadata list",e); + }finally { + if(dos != null) + try { + dos.close(); + } catch (IOException e) { + Log.e("QuizMetadataManager","Cannot close the QuizMetadataSaver's DataOutputStream",e); + } + } + } + + public static void retieveSavedQuizList(Context ctx) { + savedQuizs = new ArrayList<>(); + try { + //LATER implement bernard API + if(!ArrayInutil.inArray(SAVED_METADATA_FILENAME,ctx.fileList())){ + Log.v("QuizMetadataManager","Cannot find the savedMetadata file , creating one"); + DataOutputStream dos = new DataOutputStream(ctx.openFileOutput(SAVED_METADATA_FILENAME,Context.MODE_PRIVATE)); + dos.write(FileManager.bernardSignature); + dos.write(FileManager.appBernardKey); + dos.writeInt(-1);// Metadata file identifier + dos.writeInt(0);// Stored QuizMetadata count (no metadata stored) + dos.close(); + Log.v("QuizMetadataManager","New metadata file created !"); + } + DataInputStream dis = new DataInputStream(ctx.openFileInput(SAVED_METADATA_FILENAME)); + + int readBytes; + + byte[] bernardSignature = new byte[2]; + readBytes = dis.read(bernardSignature); + if (readBytes != 2 || bernardSignature[0] != FileManager.bernardSignature[0] || bernardSignature[1] != FileManager.bernardSignature[1]) { + dis.close(); + throw new IOException("The QuizMetatatas file has no bernard sign !"); + } + + byte[] bernardType = new byte[2]; + readBytes = dis.read(bernardType); + if (readBytes != 2 ||bernardType[0] != FileManager.appBernardKey[0] || bernardType[1] != FileManager.appBernardKey[1]) { + dis.close(); + throw new IOException("The QuizMetatatas file has no app sign !"); + } + + if (dis.readInt() != -1) { + dis.close(); + throw new IOException("The QuizMetatatas file has no quizmetadata file sign !"); + } + + int quizCount = dis.readInt(); + + for (int i = 0; i < quizCount; i++) { + savedQuizs.add(QuizMetadata.class.newInstance()); + savedQuizs.get(i).read(dis); + } + dis.close(); + Log.v("QuizMetadataManager","Successfuly read quiz metadata file"); + } catch (IOException e) { + Log.e("QuizMetadataManager", "Corrupted QuizMetadata file !",e); + } catch (InstantiationException e) { + Log.e("QuizMetadataManager","Cannot read quizmetadata file : cannot instantiate QuizMetadata",e); + } catch (IllegalAccessException e) { + Log.e("QuizMetadataManager","Cannot read quizmetadata file : cannot acess QuizMetadata constructor",e); + } + } + + //////////// Get //////////// + + public static List getAll(Context ctx){ + if(savedQuizs == null) + retieveSavedQuizList(ctx); + return new ArrayList<>(savedQuizs); + } + + public static List getAllQuiz(Context ctx,boolean editable){ + if(savedQuizs == null) + retieveSavedQuizList(ctx); + List toReturn = new ArrayList<>(); + for (QuizMetadata metadata : savedQuizs) + if(metadata.isEditable() == editable)toReturn.add(metadata); + return toReturn; + } + + public static List getAllQuiz(Context ctx,QuizMetadata.Type t){ + if(savedQuizs == null) + retieveSavedQuizList(ctx); + List toReturn = new ArrayList<>(); + for (QuizMetadata metadata : savedQuizs) + if(metadata.getType().equals(t))toReturn.add(metadata); + return toReturn; + } + + @Nullable + public static QuizMetadata getQuiz(Context ctx,long fileID){ + if(savedQuizs == null) + retieveSavedQuizList(ctx); + for (QuizMetadata metadata : savedQuizs) + if(metadata.getFileID() == fileID)return metadata; + return null; + } + + + //////////// Delete //////////// + + public static boolean delete(QuizMetadata m) { + for(QuizMetadata meta : savedQuizs) + if(meta.getFileID() == m.getFileID()) + return savedQuizs.remove(meta); + return false; + } + + + //////////// Create //////////// + + public static QuizMetadata createQuiz(Context ctx, String name, String author, QuizMetadata.Type type, boolean editable){ + try { + Quiz q = type.getQuizClass().newInstance(); + q.genDefault(); + QuizMetadata meta = new QuizMetadata(name,author,FileManager.getAvalivableFileID(ctx),type,editable); + FileManager.saveQuiz(meta,q,ctx); + savedQuizs.add(meta); + return meta; + } catch (InstantiationException e) { + Log.e("QuizMetadataManager","Error instantiating some class ... do you have all the constructors needed ?",e); + } catch (IllegalAccessException e) { + Log.e("QuizMetadataManager","Error instantiating some class ... are the constructors public if they have to be so ?",e); + } + throw new IllegalStateException("Cannot create the quiz ... watch earlier in the log for more informations"); + } + + public static QuizMetadata createQuiz(Context ctx,String name, String author, boolean editable, QuizMetadata parent){ + try { + Quiz parentQuiz = FileManager.getQuiz(parent,ctx); + QuizMetadata nMetadata = new QuizMetadata(name,author,FileManager.getAvalivableFileID(ctx),parent.getType(),editable); + FileManager.saveQuiz(nMetadata,parentQuiz,ctx); + savedQuizs.add(nMetadata); + return nMetadata; + } catch (IOException e) { + Log.e("QuizMetadataManager","IO error when trying to create the quizz",e); + } + throw new IllegalStateException("Cannot create the quiz ... watch earlier in the log for more informations"); + } + + +} diff --git a/app/src/main/java/com/bernard/mcqinator/inutil/ArrayInutil.java b/app/src/main/java/com/bernard/mcqinator/inutil/ArrayInutil.java new file mode 100644 index 0000000..7801d1b --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/inutil/ArrayInutil.java @@ -0,0 +1,158 @@ +package com.bernard.mcqinator.inutil; + +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * @author Mysaa + * @date on 22/07/17. + */ + +public class ArrayInutil { + + public static List asize(List items, int length, Integer[] failSumsArray, int failSum, Random r){ + if(Arrays.class.equals(items.getClass().getEnclosingClass())) + items = new ArrayList<>(items); + return asizeP(items,length,failSumsArray,failSum,r); + } + + public static List asize(X[] items,int length,Integer[] failSumsArray,int failSum,Random r){ + return asize(Arrays.asList(items),length,failSumsArray,failSum,r); + } + + private static List asizeP(List items,int length,Integer[] failSumsArray,int failSum,Random r){ + Log.d("asizement","asizeP("+items.toString()+","+length+","+Arrays.toString(failSumsArray)+","+failSum+",random)"); + ArrayList failSums = new ArrayList<>(Arrays.asList(failSumsArray)); + ArrayList finalItems = new ArrayList<>(); + if(items.size() <= 0)Log.e("ArrayInutil","List empty !"+items.add(null)); + if(items.size() < length){ + int n = (int) Math.floor(length / items.size()); + for (int i = 0; i < n; i++) + finalItems.addAll(items); + } + int currentFailSum = failSum; + while(finalItems.size()= ran){ + currentFailSum -= failSums.get(i); + finalItems.add(items.get(i)); + failSums.remove(i); + items.remove(i); + break; + } + } + } + Collections.shuffle(finalItems,r); + return finalItems; + } + + public static Map count(List items){ + Map out = new HashMap<>(); + for (X x:items) { + X key = null; + for (X k:out.keySet()) + if(x.equals(k)) + key = k; + if (key == null) + out.put(x, 1); + else + out.put(key, out.get(x) + 1); + } + return out; + } + + public static Long[] toObjectArray(long[] array){ + Long[] out = new Long[array.length]; + for (int i = 0; i < out.length; i++) + out[i] = array[i]; + return out; + } + + public static Integer[] toObjectArray(int[] array){ + Integer[] out = new Integer[array.length]; + for (int i = 0; i < out.length; i++) + out[i] = array[i]; + return out; + } + + public static Long[][] toObjectArray(long[][] array){ + Long[][] out = new Long[array.length][]; + for (int i = 0; i < out.length; i++) + out[i] = toObjectArray(array[i]); + return out; + } + + public static Integer[][] toObjectArray(int[][] array){ + Integer[][] out = new Integer[array.length][]; + for (int i = 0; i < out.length; i++) + out[i] = toObjectArray(array[i]); + return out; + } + + public static long[] toPrimitiveArray(Long[] array){ + long[] out = new long[array.length]; + for (int i = 0; i < out.length; i++) + out[i] = array[i]; + return out; + } + + public static int[] toPrimitiveArray(Integer[] array){ + int[] out = new int[array.length]; + for (int i = 0; i < out.length; i++) + out[i] = array[i]; + return out; + } + + public static long[][] toPrimitiveArray(Long[][] array){ + long[][] out = new long[array.length][]; + for (int i = 0; i < out.length; i++) + out[i] = toPrimitiveArray(array[i]); + return out; + } + + public static int[][] toPrimitiveArray(Integer[][] array){ + int[][] out = new int[array.length][]; + for (int i = 0; i < out.length; i++) + out[i] = toPrimitiveArray(array[i]); + return out; + } + + public static boolean inArray (T needle, T[] haystack){ + for(T item : haystack) + if(item.equals(needle))return true; + return false; + } + + public static Integer[] range (int low, int high){ + Integer[] result = new Integer[high-low]; + for(int i = 0;i int indexOf(T[] haystack, T needle) { + for (int i = 0; i < haystack.length; i++) { + if(haystack[i] != null && haystack[i].equals(needle)) + return i; + } + return -1; + } + + public static void removeIndex(T[] array, int index) { + System.arraycopy(array,index+1,array,index, array.length-index); + } + + + +} diff --git a/app/src/main/java/com/bernard/mcqinator/inutil/ClassInutil.java b/app/src/main/java/com/bernard/mcqinator/inutil/ClassInutil.java new file mode 100644 index 0000000..9f5c79d --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/inutil/ClassInutil.java @@ -0,0 +1,30 @@ +package com.bernard.mcqinator.inutil; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author samy + * @date on 23/07/17. + */ + +public class ClassInutil { + + public static Class[] getAllInterface(Class c){ + List clazz = getAllInterface(c,new ArrayList()); + Class[] clazarray = new Class[clazz.size()]; + clazz.toArray(clazarray); + return clazarray; + } + + private static List getAllInterface(Class c,List l){ + for(Class k : c.getInterfaces()){ + l.add(k); + getAllInterface(k,l); + } + if(c.getSuperclass() != null) + getAllInterface(c.getSuperclass(),l); + return l; + } + +} diff --git a/app/src/main/java/com/bernard/mcqinator/inutil/NumberInutil.java b/app/src/main/java/com/bernard/mcqinator/inutil/NumberInutil.java new file mode 100644 index 0000000..2b00d90 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/inutil/NumberInutil.java @@ -0,0 +1,31 @@ +package com.bernard.mcqinator.inutil; + +import java.text.DecimalFormat; + +/** + * @author samy + * @date on 23/07/17. + */ + +public final class NumberInutil { + + private static final DecimalFormat zero19 = new DecimalFormat("0000000000000000000"); + + /** + * + * @author JohnS from stackoverflow + */ + public static String toUnsignedString(long x) { + if (x >= 0) + return "0" + zero19.format(x); + else if (x >= -8446744073709551616L) + return "1" + zero19.format(x + 8446744073709551616L); + else + return "09" + (x - 9000000000000000000L); + } + + public static int exclude(int n,int toExclude){ + return (n < toExclude)?n:n+1; + } + +} diff --git a/app/src/main/java/com/bernard/mcqinator/inutil/Pair.java b/app/src/main/java/com/bernard/mcqinator/inutil/Pair.java new file mode 100644 index 0000000..5afb969 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/inutil/Pair.java @@ -0,0 +1,71 @@ +package com.bernard.mcqinator.inutil; + +public class Pair { + + public static Pair makePair(P p, Q q) { + return new Pair<>(p, q); + } + + public final A a; + public final B b; + + public Pair(A a, B b) { + this.a = a; + this.b = b; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((a == null) ? 0 : a.hashCode()); + result = prime * result + ((b == null) ? 0 : b.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + @SuppressWarnings("rawtypes") + Pair other = (Pair) obj; + if (a == null) { + if (other.a != null) { + return false; + } + } else if (!a.equals(other.a)) { + return false; + } + if (b == null) { + if (other.b != null) { + return false; + } + } else if (!b.equals(other.b)) { + return false; + } + return true; + } + + public boolean isInstance(Class classA, Class classB) { + return classA.isInstance(a) && classB.isInstance(b); + } + + @SuppressWarnings("unchecked") + public static Pair cast(Pair pair, Class

pClass, Class qClass) { + + if (pair.isInstance(pClass, qClass)) { + return (Pair) pair; + } + + throw new ClassCastException(); + + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/inutil/android/WaitingDialog.java b/app/src/main/java/com/bernard/mcqinator/inutil/android/WaitingDialog.java new file mode 100644 index 0000000..1080f25 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/inutil/android/WaitingDialog.java @@ -0,0 +1,110 @@ +package com.bernard.mcqinator.inutil.android; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.bernard.mcqinator.R; + +import java.util.Stack; + +/** + * @author samy + * @date on 04/08/17. + */ + +public class WaitingDialog extends Dialog { + + public WaitingDialog(@NonNull Context context) { + super(context); + } + LinearLayout ll; + Stack barsLayout; + ViewGroup parent; + LayoutInflater inflater; + Activity a; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.waitdialog_layout); + this.inflater = getLayoutInflater(); + ll = findViewById(R.id.waitList); + + barsLayout = new Stack<>(); + } + + private Activity ownerActivity(){ + Activity oA = getOwnerActivity(); + if(oA == null) + throw new IllegalStateException("The owner's activity is null ... and it should not"); + return oA; + } + + public int newBar(){ + ownerActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + ViewGroup barLayout = (ViewGroup)inflater.inflate(R.layout.waitdialog_progressbar,ll,true); + barsLayout.push(barLayout); + + } + }); + return barsLayout.size() + 1; + } + + public int newBar(int max){ + newBar(); + setMax(max); + return barsLayout.size(); + } + + private ProgressBar getProgressBar(){ + return ((ProgressBar)barsLayout.peek().findViewById(R.id.progressBar)); + } + + private TextView getProgressText(){ + return ((TextView)barsLayout.peek().findViewById(R.id.progressText)); + } + + public void setMax(final int max){ + ownerActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + ProgressBar bar = getProgressBar(); + bar.setMax(max); + getProgressText().setText(bar.getProgress() + "/" + bar.getMax()); + } + }); + + } + + public void setValue(final int value){ + ownerActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + ProgressBar bar = getProgressBar(); + bar.setProgress(value); + getProgressText().setText(bar.getProgress() + "/" + bar.getMax()); + } + }); + } + + public int removeBar(){ + ownerActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + ll.removeView(barsLayout.pop()); + } + }); + + return barsLayout.size() - 1; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bernard/mcqinator/view/QuizAdapter.java b/app/src/main/java/com/bernard/mcqinator/view/QuizAdapter.java new file mode 100644 index 0000000..76e9e2c --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/QuizAdapter.java @@ -0,0 +1,85 @@ +package com.bernard.mcqinator.view; + +import android.content.Context; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.QuizMetadata; + +import java.util.List; + +/** + * @author Mysaa + * @date on 17/04/17. + */ + +public class QuizAdapter extends BaseAdapter { + + private final List items; + private final Context ctx; + + public QuizAdapter(List quizz,Context context){ + this.items = quizz; + this.ctx = context; + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public QuizMetadata getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return 42;//Use is up to the developer + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final View mainView; + if(convertView != null) + mainView = convertView; + else + mainView = LayoutInflater.from(ctx).inflate(R.layout.quiz_row_layout,parent,false); + + TextView nameText; + TextView authorText; + ImageView iconView; + + try { + nameText = mainView.findViewById(R.id.quizAdapterName); + authorText = mainView.findViewById(R.id.quizAdapterAuthor); + iconView = mainView.findViewById(R.id.quizAdapterIcon); + + if (nameText == null) + throw new RuntimeException("Failed to find view with ID quizAdapterName in layout"); + if (authorText == null) + throw new RuntimeException("Failed to find view with ID quizAdapterAuthor in layout"); + if (iconView == null) + throw new RuntimeException("Failed to find view with ID quizAdapterIcon in layout"); + + } catch (ClassCastException e) { + Log.e("QuizAdapter", "Id quizAdapterName and quizAdapterAuthor must be a TextView and quizAdapterIcon must be an ImageView"); + throw new IllegalStateException("QuizAdapter'layout ids didn't match the right types", e); + } + + final QuizMetadata quiz = getItem(position); + if(quiz != null) { + nameText.setText(quiz.getName()); + authorText.setText(quiz.getAuthor()); + iconView.setImageDrawable(ContextCompat.getDrawable(ctx,quiz.getType().getQuizIconID())); + } + return mainView; + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/TextBackgroundManager.java b/app/src/main/java/com/bernard/mcqinator/view/TextBackgroundManager.java new file mode 100644 index 0000000..e8aca32 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/TextBackgroundManager.java @@ -0,0 +1,168 @@ +package com.bernard.mcqinator.view; + +import android.app.Activity; +import android.preference.PreferenceManager; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.animation.Animation; +import android.view.animation.TranslateAnimation; +import android.widget.FrameLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.bernard.mcqinator.R; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * @author Mysaa + * @date 17/04/17. + */ + +public final class TextBackgroundManager { + + private static final Random textRandom = new Random(); + private static String[] deWorte; + private Integer[] worteYArray; + private final Activity activity; + private TextView[] floatingTexts; + + + + public TextBackgroundManager(Activity activity) { + this.activity = activity; + //TODO rearrange preferences + switch(PreferenceManager.getDefaultSharedPreferences(activity).getInt("preferedTheme",0)){ + case 0: + activity.setTheme(R.style.DarkTheme); + break; + case 1: + activity.setTheme(R.style.LightTheme); + break; + } + if(deWorte == null) + deWorte = activity.getResources().getStringArray(R.array.randomWorte); + final View rout = activity.findViewById(android.R.id.content); + rout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + // gets called after layout has been done but before display + /* + TypedArray styledAttributes = TextBackgroundManager.this.activity.obtainStyledAttributes(new int[]{android.R.attr.textSize}); + Pattern p = Pattern.compile("^[0-9]+"); + String textSize = styledAttributes.hasValue(0)?styledAttributes.getString(0):"12"; + Matcher m = p.matcher(textSize); + Log.d("TXTS",textSize); + */ + final int txtHeight = 20; + final int totalHeight = rout.getHeight(); + int currentPos = textRandom.nextInt(txtHeight); + List tempYArray = new ArrayList<>(); + while (currentPos < totalHeight) { + tempYArray.add(currentPos); + currentPos += txtHeight; + currentPos += textRandom.nextInt(8) - 4; + } + worteYArray = new Integer[tempYArray.size()]; + tempYArray.toArray(worteYArray); + rout.getViewTreeObserver().removeGlobalOnLayoutListener(this); + floatingTexts = new TextView[worteYArray.length]; + for (int i = 0; i < worteYArray.length; i++) + floatingTexts[i] = launchText(i); + } + }); + } + + private TextView launchText(int x){ + final TextView tv = new TextView(activity); + tv.setText(deWorte[textRandom.nextInt(deWorte.length)]); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT); + params.leftMargin = -tv.getWidth(); + params.topMargin = worteYArray[x]; + tv.setLayoutParams(params); + ((ViewGroup)activity.findViewById(android.R.id.content)).addView(tv,0); + tv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + + TranslateAnimation anim = new TranslateAnimation(-tv.getWidth(), tv.getWidth(), 0, 0); + anim.setDuration(8000 + textRandom.nextInt(1000)); + anim.setStartOffset(textRandom.nextInt(9000)); + anim.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + tv.setText(deWorte[textRandom.nextInt(deWorte.length)]); + TranslateAnimation anim = new TranslateAnimation(-tv.getWidth(), tv.getWidth(), 0, 0); + anim.setDuration(8000 + textRandom.nextInt(1000)); + anim.setAnimationListener(this); + tv.startAnimation(anim); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + tv.startAnimation(anim); + tv.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + }); + return tv; + } + + public void stopTexts(){ + if(floatingTexts == null)return; + for (TextView t:floatingTexts) { + t.clearAnimation(); + } + } + + public void restartTexts(){ + if(floatingTexts == null)return; + for (final TextView tv:floatingTexts) { + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT); + params.leftMargin = -tv.getWidth(); + params.topMargin = ((FrameLayout.LayoutParams)tv.getLayoutParams()).topMargin; + tv.setLayoutParams(params); + tv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + + TranslateAnimation anim = new TranslateAnimation(-tv.getWidth(), tv.getWidth(), 0, 0); + anim.setDuration(8000 + textRandom.nextInt(1000)); + anim.setStartOffset(textRandom.nextInt(9000)); + anim.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + tv.setText(deWorte[textRandom.nextInt(deWorte.length)]); + TranslateAnimation anim = new TranslateAnimation(-tv.getWidth(), tv.getWidth(), 0, 0); + anim.setDuration(8000 + textRandom.nextInt(1000)); + anim.setAnimationListener(this); + tv.startAnimation(anim); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }); + tv.startAnimation(anim); + tv.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + }); + } + } + + public static Random getRandom() { + return textRandom; + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/ChoosingQuizActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/ChoosingQuizActivity.java new file mode 100644 index 0000000..3cdd1f6 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/ChoosingQuizActivity.java @@ -0,0 +1,47 @@ +package com.bernard.mcqinator.view.activities; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ListView; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.view.QuizAdapter; +import com.bernard.mcqinator.view.TextBackgroundManager; + +public class ChoosingQuizActivity extends ListActivity { + + TextBackgroundManager tbm; + QuizAdapter qa; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_choosing_quiz); + tbm = new TextBackgroundManager(this); + + qa = new QuizAdapter(QuizMetadataManager.getAll(this),this); + setListAdapter(qa); + } + + @Override + protected void onStart() { + super.onStart(); + tbm.stopTexts(); + } + + @Override + protected void onRestart() { + super.onRestart(); + tbm.restartTexts(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Intent i = new Intent(this,ConfiguringQuizActivity.class); + i.putExtra(ConfiguringQuizActivity.QUIZID,qa.getItem(position).getFileID()); + startActivity(i); + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/ChoosingQuizToEditActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/ChoosingQuizToEditActivity.java new file mode 100644 index 0000000..4649d59 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/ChoosingQuizToEditActivity.java @@ -0,0 +1,71 @@ +package com.bernard.mcqinator.view.activities; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ListView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.view.TextBackgroundManager; +import com.bernard.mcqinator.view.QuizAdapter; +import com.bernard.mcqinator.api.QuizMetadata; + +public class ChoosingQuizToEditActivity extends ListActivity { + + public static final String QUIZ_FILEID = "com.bernard.qcminator.quizFileID"; + + TextBackgroundManager tbm; + QuizAdapter qa; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_choosing_quiz_to_edit); + tbm = new TextBackgroundManager(this); + + Button createQuizButton = findViewById(R.id.newQuizButton); + createQuizButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(ChoosingQuizToEditActivity.this,CreateQuizActivity.class); + //No data to transfer + startActivity(i); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + qa = new QuizAdapter(QuizMetadataManager.getAllQuiz(this,true), this); + setListAdapter(qa); + tbm.stopTexts(); + } + + @Override + protected void onRestart() { + super.onRestart(); + tbm.restartTexts(); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + QuizMetadata selected = qa.getItem(position); + + if(selected == null) { + Log.e("EditQuiz","Trying to edit a null quiz ... aborting"); + Toast.makeText(this, "Cannot edit quiz", Toast.LENGTH_SHORT).show(); + return; + } + Intent i = new Intent(ChoosingQuizToEditActivity.this,selected.getType().getEditActivityClass()); + + i.putExtra(ChoosingQuizToEditActivity.QUIZ_FILEID,selected.getFileID()); + + startActivity(i); + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/ConfiguringQuizActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/ConfiguringQuizActivity.java new file mode 100644 index 0000000..b400b04 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/ConfiguringQuizActivity.java @@ -0,0 +1,237 @@ +package com.bernard.mcqinator.view.activities; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.IdentifiedQuiz; +import com.bernard.mcqinator.api.QuizMetadata; +import com.bernard.mcqinator.api.frageable.Frageable; +import com.bernard.mcqinator.api.fragen.Frage; +import com.bernard.mcqinator.api.fragenStats.QuizStat; +import com.bernard.mcqinator.api.quiz.Quiz; +import com.bernard.mcqinator.controller.FileManager; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.inutil.ClassInutil; +import com.bernard.mcqinator.view.activities.congratsActivity.CongratsActivity; +import com.bernard.mcqinator.view.activities.fragenActivities.FrageActivity; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ConfiguringQuizActivity extends Activity implements AdapterView.OnItemClickListener,View.OnClickListener{ + + public static final String QUIZID = "com.bernard.qcminator.QUIZID"; + private static final int MAKE_QUIZ = 0x6C98E2AA; + private static final int CONGRATS_QUIZ = 0x2843124F; + public ProgressDialog fragment; + + QuizMetadata q; + Quiz quiz; + + FrameLayout frame; + ListView list; + View actualView; + Button go; + Frage[] fragen; + LinkedHashMap classes;// + LinkedHashMap configurators;// + private Class actualClass; + private Frage.Identifier storedIdentifier; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_configuring_quiz); + long quizID = getIntent().getLongExtra(QUIZID,-1L); + if(quizID == -1L)throw new IllegalArgumentException("Missing long "+QUIZID+" in the intent"); + q = QuizMetadataManager.getQuiz(this,quizID); + + frame = findViewById(R.id.attributesFrame); + list = findViewById(android.R.id.list); + go = findViewById(R.id.doneButton); + + fragment = new ProgressDialog(ConfiguringQuizActivity.this); + + classes = new LinkedHashMap<>(); + configurators = new LinkedHashMap<>(); + + for (Class i : ClassInutil.getAllInterface(q.getType().getQuizClass())) { + if(!Frageable.class.isAssignableFrom(i)) + continue; + try { + Class configuratorClass = Class.class.cast(i.getField("CONFIGURATOR").get(null)); + Frageable.Configurator configurator = (Frageable.Configurator) configuratorClass.newInstance(); + View added = configurator.attachConfigLayout(this,frame); + added.setVisibility(View.GONE); + configurators.put(i,configurator); + classes.put(i,added); + } catch (IllegalAccessException e) { + Log.e("ClassError","The field CONFIGURATOR of the class "+i+" is not public"); + } catch (NoSuchFieldException e) { + Log.e("ClassError","The class "+i+" don't have the field CONFIGURATOR"); + } catch (InstantiationException e) { + Log.e("ClassError","Cannot instantiate the CONFIGURATOR of class "+i+" , do you have a public constructor ?"); + } catch (ClassCastException e) { + Log.e("ClassError","The CONFIGURATOR of class "+i+" is not a Class"); + } + + } + Log.d("ConfiguringQuizActivity","Quiz Frageable Classes : " + classes.toString()); + list.setAdapter(new QuizFrageableAdapter()); + list.setOnItemClickListener(this); + + go.setOnClickListener(this); + } + + + protected void launchQuiz(final Frageable.Data data){ + final Context me = this; + Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + quiz = FileManager.getQuiz(q,me); + } catch (IOException e) { + Log.e("ConfiguringQuizActivity","Cannot retieve Quiz data ... aborting quiz launch",e); + } + if(quiz == null) + Toast.makeText(me, R.string.cannotLaunchQuiz, Toast.LENGTH_SHORT).show(); + + runOnUiThread(new Runnable() { + @Override + public void run() { + fragment.setTitle("WaitingDialog"); + fragment.setMessage("Chargement de votre quiz en cours ..."); + fragment.setIndeterminate(true); + fragment.show(); + } + }); + + Frageable f = (Frageable)quiz; + + QuizStat stats = FileManager.getStat(q,me); + if(stats == null){ + stats = QuizStat.createStat(q.getType().getQuizStatClass(),quiz); + FileManager.saveStat(q,stats,me); + } + IdentifiedQuiz identifiedQuiz = checkedGenFragen(f,stats,data,fragment); + storedIdentifier = identifiedQuiz.getIdentifier(); + fragen = identifiedQuiz.getFragen(); + runOnUiThread(new Runnable() { + @Override + public void run() { + fragment.hide(); + Intent i = new Intent(ConfiguringQuizActivity.this,data.getFrageActivityClass()); + i.putExtra(FrageActivity.FRAGEN,fragen); + i.putExtra(FrageActivity.QUIZ_DATA,data); + startActivityForResult(i,ConfiguringQuizActivity.MAKE_QUIZ); + } + }); + } + },"QuizLauncherThread"); + t.start(); + } + + @SuppressWarnings("unchecked") + private IdentifiedQuiz checkedGenFragen(Frageable f, QuizStat stats, Frageable.Data data, ProgressDialog wait){ + return f.genFragen(stats,data,this,wait);//Normal Warning + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if(requestCode == MAKE_QUIZ && resultCode == RESULT_OK){ + ArrayList repliesList = data.getParcelableArrayListExtra(FrageActivity.REPLIES); + QuizStat stats = FileManager.getStat(q,this); + stats = checkedUpdateStats((Frageable) quiz,stats,repliesList,storedIdentifier); + FileManager.saveStat(q,stats,this); + Intent i = new Intent(ConfiguringQuizActivity.this,q.getType().getCongratsActivityClass()); + i.putExtra(CongratsActivity.FRAGEN,fragen); + i.putParcelableArrayListExtra(CongratsActivity.REPLIES,repliesList); + i.putExtra(CongratsActivity.QUIZ_DATA,q); + startActivityForResult(i,ConfiguringQuizActivity.CONGRATS_QUIZ); + }else if(requestCode == CONGRATS_QUIZ && resultCode == RESULT_OK){ + Log.d("ConfiguringQuizActivity","Congrats made !"); + } + } + + @SuppressWarnings("unchecked") + private QuizStat checkedUpdateStats(Frageable f, QuizStat stats, List replies, Frage.Identifier identifier){ + return f.updateStats(stats,replies,identifier);//Normal Warning + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if(actualView != null) + actualView.setVisibility(View.GONE); + Map.Entry[] entries = new Map.Entry[classes.size()]; + classes.entrySet().toArray(entries); + actualClass = (Class) entries[position].getKey(); + actualView = classes.get(actualClass); + actualView.setVisibility(View.VISIBLE); + } + + @Override + public void onClick(View v) { + if(actualClass == null)return; + Frageable.Configurator configurator = configurators.get(actualClass); + Frageable.Data data = configurator.genDataFromConfigLayout(actualView); + launchQuiz(data); + + } + + private class QuizFrageableAdapter extends BaseAdapter{ + + @Override + public int getCount() { + return classes.size(); + } + + @Override + public Class getItem(int position) { + return (Class) classes.keySet().toArray()[position]; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewGroup lay; + TextView text; + ImageView image; + if(convertView == null) + lay = (ViewGroup)getLayoutInflater().inflate(R.layout.quiz_typ_row_layout,parent,false); + else + lay = (ViewGroup)convertView; + text = lay.findViewById(R.id.quizTypText); + image = lay.findViewById(R.id.quizTypIcon); + Frageable.Configurator configurator = configurators.get(getItem(position)); + text.setText(configurator.getTypStringID()); + int drawableID = configurator.getIconDrawableID(); + image.setImageDrawable(getDrawable(drawableID)); + + + return lay; + } + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/CreateQuizActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/CreateQuizActivity.java new file mode 100644 index 0000000..86aa490 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/CreateQuizActivity.java @@ -0,0 +1,121 @@ +package com.bernard.mcqinator.view.activities; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.view.TextBackgroundManager; +import com.bernard.mcqinator.view.QuizAdapter; +import com.bernard.mcqinator.api.QuizMetadata; + +/* + TODO make text "no installed quiz" diseapear + */ +public class CreateQuizActivity extends Activity implements AdapterView.OnItemSelectedListener{ + + ArrayAdapter sadapter; + TextBackgroundManager tbm; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_create_quiz); + tbm = new TextBackgroundManager(this); + + final EditText nameText = findViewById(R.id.quizNameAsk); + final EditText authorText = findViewById(R.id.quizAuthorAsk); + + //Type select spiner + final int defaultSelectIndex = 0; + final Spinner sp = findViewById(R.id.quizTypeAsk); + sadapter = new ArrayAdapter<>(this,android.R.layout.simple_spinner_item,QuizMetadata.Type.values()); + sadapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + sp.setAdapter(sadapter); + sp.setOnItemSelectedListener(this); + sp.setSelection(defaultSelectIndex); + + //Parent quiz list + final ListView lv = findViewById(R.id.quizParentChoose); + QuizAdapter adapter = new QuizAdapter(QuizMetadataManager.getAllQuiz(this,sadapter.getItem(defaultSelectIndex)),this); + lv.setAdapter(adapter); + + //Has parent checkbox + final CheckBox hasParentBox = findViewById(R.id.quizHasParentAsk); + hasParentBox.setSelected(false); + hasParentBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + lv.setEnabled(isChecked); + } + }); + + Button go = findViewById(R.id.doneButton); + go.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + QuizMetadata.Type quizType = (QuizMetadata.Type) sp.getSelectedItem(); + String name = nameText.getText().toString(); + if(name.isEmpty()){ + Toast.makeText(CreateQuizActivity.this,R.string.quizNameEmpty,Toast.LENGTH_SHORT).show(); + return; + } + String author = authorText.getText().toString(); + if(author.isEmpty()){ + Toast.makeText(CreateQuizActivity.this,R.string.quizAuthorEmpty,Toast.LENGTH_SHORT).show(); + return; + } + Intent i = new Intent(CreateQuizActivity.this,quizType.getEditActivityClass()); + + QuizMetadata creation; + + if (hasParentBox.isChecked()) { + if(lv.getSelectedItem() == null){ + Toast.makeText(CreateQuizActivity.this,R.string.quizParentEmpty,Toast.LENGTH_SHORT).show(); + return; + } + QuizMetadata parent = (QuizMetadata) lv.getSelectedItem(); + creation = QuizMetadataManager.createQuiz(CreateQuizActivity.this,name,author,true,parent); + + } else + creation = QuizMetadataManager.createQuiz(CreateQuizActivity.this, name, author, quizType, true); + + i.putExtra(ChoosingQuizToEditActivity.QUIZ_FILEID,creation.getFileID()); + + startActivity(i); + } + }); + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + ListView lv = findViewById(R.id.quizParentChoose); + QuizAdapter adapter = new QuizAdapter(QuizMetadataManager.getAllQuiz(this,sadapter.getItem(position)),this); + lv.setAdapter(adapter); + TextView nothingText = findViewById(R.id.quizParentAskNoQuiz); + if(adapter.getCount()<1){ + lv.setVisibility(View.GONE); + nothingText.setVisibility(View.VISIBLE); + }else{ + lv.setVisibility(View.VISIBLE); + nothingText.setVisibility(View.GONE); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/MainActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/MainActivity.java new file mode 100644 index 0000000..949dbf4 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/MainActivity.java @@ -0,0 +1,108 @@ +package com.bernard.mcqinator.view.activities; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.view.TextBackgroundManager; + +public class MainActivity extends Activity { + + //TODO add a global javadoc + //TODO add a global argument check with exceptions + + TextBackgroundManager tbm; + public static final String PREFERENCES_FILE_KEY = "com.bernard.qcminator.SAXOPHONE"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + tbm = new TextBackgroundManager(this); + + final Button takeQuiz = findViewById(R.id.takeQuizButton); + takeQuiz.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(MainActivity.this,ChoosingQuizActivity.class); + //No data to transfer + startActivity(i); + } + }); + final Button manageQuiz = findViewById(R.id.manageMyQuizButton); + manageQuiz.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(MainActivity.this,QuizManagerActivity.class); + //No data to transfer + startActivity(i); + } + }); + final Button createQuiz = findViewById(R.id.createQuizButton); + createQuiz.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(MainActivity.this,ChoosingQuizToEditActivity.class); + //No data to transfer + startActivity(i); + } + }); + + final Button options = findViewById(R.id.optionsButton); + createQuiz.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //Intent i = new Intent(MainActivity.this,ChoosingQuizToEditActivity.class); + //No data to transfer + //startActivity(i); + } + }); + options.setEnabled(false); + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + QuizMetadataManager.retieveSavedQuizList(MainActivity.this); + runOnUiThread(new Runnable() { + @Override + public void run() { + findViewById(R.id.loading).setVisibility(View.GONE); + takeQuiz.setEnabled(true); + manageQuiz.setEnabled(true); + createQuiz.setEnabled(true); + //options.setEnabled(true); + + } + }); + } + }); + t.start(); + + } + + @Override + protected void onStart() { + super.onStart(); + tbm.stopTexts(); + } + + @Override + protected void onRestart() { + super.onRestart(); + tbm.restartTexts(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + QuizMetadataManager.saveSavedQuizList(this); + } + + + +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/QuizManagerActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/QuizManagerActivity.java new file mode 100644 index 0000000..9b7b847 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/QuizManagerActivity.java @@ -0,0 +1,271 @@ +package com.bernard.mcqinator.view.activities; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.ListActivity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.QuizMetadata; +import com.bernard.mcqinator.controller.FileManager; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.view.QuizAdapter; +import com.bernard.mcqinator.view.TextBackgroundManager; + +import java.io.File; +import java.io.IOException; + +public class QuizManagerActivity extends ListActivity{ + + TextBackgroundManager tbm; + public static final String VICTIM_QUIZ_FILEID = "com.bernard.qcminator.VICTIM_QUIZ_FILEID"; + public static final int CHOOSING_FILE_TO_IMPORT = 0x55BB2B3A; + QuizAdapter qa; + + //////////// Activity lifecycle //////////// + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_quiz_manager); + tbm = new TextBackgroundManager(this); + + Button importQuiz = findViewById(R.id.importQuiz); + importQuiz.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(Intent.ACTION_GET_CONTENT); + i.setType("file/*"); + try{ + startActivityForResult(Intent.createChooser(i,"Select a file to import"),CHOOSING_FILE_TO_IMPORT); + }catch(ActivityNotFoundException e){ + Toast.makeText(QuizManagerActivity.this, R.string.noFileReader, Toast.LENGTH_LONG).show(); + } + } + }); + + } + + @Override + protected void onStart() { + super.onStart(); + reloadQuizz(); + tbm.stopTexts(); + } + + @Override + protected void onRestart() { + super.onRestart(); + tbm.restartTexts(); + } + + public void reloadQuizz(){ + qa = new QuizAdapter(QuizMetadataManager.getAll(this),this); + setListAdapter(qa); + } + + + //////////// Listeners //////////// + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + DialogFragment fragment = new WhatToDoToQuiz(); + Bundle args = new Bundle(); + args.putLong(VICTIM_QUIZ_FILEID,qa.getItem(position).getFileID()); + fragment.setArguments(args); + fragment.show(getFragmentManager(),"QuizWhatToDo"); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if(requestCode == CHOOSING_FILE_TO_IMPORT && resultCode == RESULT_OK){ + String path = data.getData().getPath(); + Log.d("Import file","Importing "+path); + try { + File f = new File(path); + if(!f.canRead()){ + Toast.makeText(this, R.string.unreadableFile, Toast.LENGTH_LONG).show(); + return; + } + + QuizMetadata meta = FileManager.importQuizFromFile(this,f); + QuizMetadataManager.saveSavedQuizList(this); + assert meta != null; + Toast.makeText(this, getResources().getString(R.string.successfullyImportedQuiz, meta.getName()), Toast.LENGTH_LONG).show(); + } catch (IOException | NullPointerException | AssertionError e) { + Log.e("ImportQuiz","Failed importing quiz",e); + Toast.makeText(this, getResources().getString(R.string.failedImportQuiz), Toast.LENGTH_LONG).show(); + } + + } + } + + //////////// Internal classes //////////// + + ////// Dialogs ////// + + public static class WhatToDoToQuiz extends DialogFragment { + + @Override + public Dialog onCreateDialog(final Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setTitle(R.string.whatToDoToQuiz) + .setItems(R.array.quizManageOption, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case 0://Delete + DialogFragment fragment = new ReallyWantToDelete(); + Bundle args = new Bundle(); + args.putLong(VICTIM_QUIZ_FILEID, getArguments().getLong(VICTIM_QUIZ_FILEID)); + fragment.setArguments(args); + fragment.show(getFragmentManager(), "QuizReallyWantToDelete"); + break; + case 1://Export + QuizMetadata q = QuizMetadataManager.getQuiz(getActivity(),getArguments().getLong(VICTIM_QUIZ_FILEID)); + if(q == null){ + Log.e("ExportQuiz","Quiz not found"); + Toast.makeText(getActivity(), "Cannot export quiz ... quiz not found", Toast.LENGTH_SHORT).show(); + break; + } + String fileName; + File f; + int i = 0; + do { + fileName = q.getName() + "-" + q.getAuthor() + ((i!=0)?("("+i+")"):"") +".bernard"; + i++; + }while((f = new File(getActivity().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),fileName)).exists()); + try { + if(!f.createNewFile())throw new IOException("The file exists ... this error is nor supposed to be"); + FileManager.exportQuizToFile(q,getActivity(),f); + Toast.makeText(getActivity(), getResources().getString(R.string.successfullyExportQuizTo,fileName), Toast.LENGTH_SHORT).show(); + } catch (IOException e) { + Log.d("ExportFile","Can't export the file to "+fileName,e); + Toast.makeText(getActivity(), getResources().getString(R.string.failedExportQuizTo,fileName), Toast.LENGTH_SHORT).show(); + } + break; + case 2://Duplicate + DialogFragment fragment3 = new DuplicateOptions(); + Bundle args3 = new Bundle(); + args3.putLong(VICTIM_QUIZ_FILEID, getArguments().getLong(VICTIM_QUIZ_FILEID)); + fragment3.setArguments(args3); + fragment3.show(getFragmentManager(), "QuizReallyWantToDelete"); + break; + } + } + }); + + + return builder.create(); + } + + } + + public static class ReallyWantToDelete extends DialogFragment { + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if(getActivity() instanceof QuizManagerActivity) + ((QuizManagerActivity)getActivity()).reloadQuizz(); + } + + @Override + public Dialog onCreateDialog(final Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setMessage(R.string.reallyWantToDelete) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + QuizMetadata q = QuizMetadataManager.getQuiz(getActivity(),getArguments().getLong(VICTIM_QUIZ_FILEID)); + if (q == null) { + Log.e("DeleteQuiz", "The quiz reference was null, cannot delete quiz"); + Toast.makeText(getActivity(), R.string.cannotDeleteQuiz, Toast.LENGTH_LONG).show(); + return; + } + if (FileManager.delete(q,getActivity())) + Toast.makeText(getActivity(), getResources().getString(R.string.successfullyDeleteQuiz, q.getName()), Toast.LENGTH_LONG).show(); + else + Toast.makeText(getActivity(), getResources().getString(R.string.failedDeleteQuiz, q.getName()), Toast.LENGTH_LONG).show(); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Toast.makeText(getActivity(), android.R.string.ok, Toast.LENGTH_SHORT).show(); + } + }); + + + return builder.create(); + } + + } + + public static class DuplicateOptions extends DialogFragment { + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if(getActivity() instanceof QuizManagerActivity) + ((QuizManagerActivity)getActivity()).reloadQuizz(); + } + + @SuppressLint("InflateParams") + @Override + public Dialog onCreateDialog(final Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + LayoutInflater inflater = getActivity().getLayoutInflater(); + builder.setMessage(R.string.newDuplicateData) + .setView(inflater.inflate(R.layout.duplication_options_layout, null,false)) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Context c = getActivity(); + QuizMetadata q = QuizMetadataManager.getQuiz(getActivity(),getArguments().getLong(VICTIM_QUIZ_FILEID)); + if (q == null) { + Log.d("DuplicateQuiz", "The quiz reference was null, cannot duplicate quiz"); + Toast.makeText(getActivity(), R.string.failedDuplicateQuiz, Toast.LENGTH_LONG).show(); + return; + } + String newName = ((EditText)getDialog().findViewById(R.id.newNameAsk)).getText().toString(); + String newAuthor = ((EditText)getDialog().findViewById(R.id.newAuthorAsk)).getText().toString(); + if(newAuthor.isEmpty()) + newAuthor = q.getAuthor(); + try { + QuizMetadataManager.createQuiz(c,newName,newAuthor,true,q); + Toast.makeText(getActivity(), getResources().getString(R.string.successfullyDuplicateQuiz, q.getName()), Toast.LENGTH_LONG).show(); + } catch (Exception e) { + Log.e("Quizduplication","Cannot duplicate quiz : ",e); + Toast.makeText(getActivity(), getResources().getString(R.string.failedDuplicateQuiz), Toast.LENGTH_LONG).show(); + } + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Toast.makeText(getActivity(), android.R.string.ok, Toast.LENGTH_SHORT).show(); + } + }); + + + + return builder.create(); + } + + } + +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/congratsActivity/CongratsActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/congratsActivity/CongratsActivity.java new file mode 100644 index 0000000..e5cf627 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/congratsActivity/CongratsActivity.java @@ -0,0 +1,45 @@ +package com.bernard.mcqinator.view.activities.congratsActivity; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Parcelable; +import android.support.annotation.Nullable; + +import com.bernard.mcqinator.api.QuizMetadata; +import com.bernard.mcqinator.api.fragen.Frage; + +import java.util.List; + +/** + * @author Mysaa + * @date on 27/07/17. + */ + +public abstract class CongratsActivity extends Activity { + + public static final String FRAGEN = "com.bernard.qcminator.FRAGEN"; + public static final String REPLIES = "com.bernard.qcminator.REPLIES"; + public static final String QUIZ_DATA = "com.bernard.qcminator.QUIZ_DATA"; + + QuizMetadata meta; + Frage[] fragen; + List replies; + + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //TODO check intent + meta = getIntent().getParcelableExtra(QUIZ_DATA); + Parcelable[] pfragen = getIntent().getParcelableArrayExtra(FRAGEN); + fragen = new Frage[pfragen.length]; + for (int i = 0; i < pfragen.length; i++)fragen[i] = (Frage)pfragen[i]; + replies = getIntent().getParcelableArrayListExtra(REPLIES); + + create(savedInstanceState); + } + + public abstract void create(@Nullable Bundle savedInstanceState); + + +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/congratsActivity/DEVerbenCongratsActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/congratsActivity/DEVerbenCongratsActivity.java new file mode 100644 index 0000000..7c28186 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/congratsActivity/DEVerbenCongratsActivity.java @@ -0,0 +1,85 @@ +package com.bernard.mcqinator.view.activities.congratsActivity; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.text.Html; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.fragen.BlocFrage; + +public class DEVerbenCongratsActivity extends CongratsActivity implements View.OnClickListener{ + + ListView deverben; + TextView score; + Button doneButton; + DEVerbenReplyadapter adapter; + + + @Override + public void create(@Nullable Bundle savedInstanceState) { + setContentView(R.layout.activity_deverben_congrats); + deverben = findViewById(R.id.verbeLayout); + score = findViewById(R.id.noteText); + int z = 0; + for (int i = 0; i < replies.size(); i++)if(((BlocFrage.Reply) replies.get(i)).getAnswered().equals(((BlocFrage)fragen[i]).getRichtigAntwort()))z++; + score.setText(z + "/" + replies.size()); + adapter = new DEVerbenReplyadapter(); + deverben.setAdapter(adapter); + doneButton = findViewById(R.id.doneButton); + doneButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + finish(); + } + + public class DEVerbenReplyadapter extends BaseAdapter{ + + @Override + public int getCount() { + return replies.size(); + } + + @Override + public Object getItem(int position) { + return replies.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewGroup lay; + TextView text; + if(convertView == null) + lay = (ViewGroup)getLayoutInflater().inflate(R.layout.deverben_reply_row_layout,parent,false); + else + lay = (ViewGroup)convertView; + text = lay.findViewById(R.id.deverbenRepyText); + BlocFrage frage = (BlocFrage)fragen[position]; + BlocFrage.Reply reply = (BlocFrage.Reply) replies.get(position); + boolean correct = reply.getAnswered().equals(frage.getRichtigAntwort()); + text.setGravity(Gravity.CENTER); + if(correct){ + text.setText(frage.getRichtigAntwort()); + text.setBackgroundColor(getResources().getColor(R.color.goodAnswer)); + }else{ + text.setText(Html.fromHtml("" + reply.getAnswered() + " -> " + frage.getRichtigAntwort())); + text.setBackgroundColor(getResources().getColor(R.color.wrongAnswer)); + } + return lay; + } + } + +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/fragenActivities/BlocFrageActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/fragenActivities/BlocFrageActivity.java new file mode 100644 index 0000000..0f91582 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/fragenActivities/BlocFrageActivity.java @@ -0,0 +1,80 @@ +package com.bernard.mcqinator.view.activities.fragenActivities; + +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.frageable.BlocFrageable; +import com.bernard.mcqinator.api.fragen.BlocFrage; + +import java.util.ArrayList; +import java.util.Arrays; + +public class BlocFrageActivity extends FrageActivity implements View.OnClickListener{ + + TextView frage; + TableLayout tl; + Button[] buttons; + + @Override + public void initiate(BlocFrage f) { + ArrayList replies = new ArrayList<>(); + replies.addAll(Arrays.asList(f.getFalshAntworte())); + replies.add(f.getRichtigAntwort()); + Log.d("Buttons",Arrays.toString(buttons)); + for (Button button : buttons) { + int pos = (int) (Math.random() * (double) replies.size()); + button.setText(replies.get(pos)); + replies.remove(pos); + } + frage.setText(f.getFrage()); + } + + @Override + @SuppressWarnings("null") + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bloc_frage); + + BlocFrageable.BlocData data = getIntent().getParcelableExtra(QUIZ_DATA); + int bn = data.blockNumber; + + frage= findViewById(R.id.frageText); + tl= findViewById(R.id.blocTable); + + TableRow tr = null; + buttons = new Button[bn]; + TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT,TableRow.LayoutParams.WRAP_CONTENT); + params.weight = 50; + TableLayout.LayoutParams tparams = new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT); + for(int b = 0;b extends Activity { + + public static final String FRAGEN = "com.bernard.qcminator.FRAGEN"; + public static final String REPLIES = "com.bernard.qcminator.REPLIES"; + public static final String QUIZ_DATA = "com.bernard.qcminator.QUIZ_DATA"; + + List fragen; + private List replies; + private int currentFrage = 0; + public abstract void initiate(T f); + + @Override + @SuppressWarnings("unchecked") + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Parcelable[] preFragen = getIntent().getParcelableArrayExtra(FRAGEN); + fragen = new ArrayList<>(); + for (Parcelable aPreFragen : preFragen) fragen.add((T) aPreFragen); + Log.v("FrageActivity","Fragen : "+fragen); + replies = new ArrayList<>(); + final View rout = findViewById(android.R.id.content); + rout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + initiate(fragen.get(currentFrage)); + rout.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + }); + } + + public void reply(Frage.Reply reply){ + replies.add(reply); + + currentFrage++; + if(currentFrage == fragen.size()) { + Intent i = new Intent(); + i.putParcelableArrayListExtra(REPLIES,(ArrayList)replies); + setResult(RESULT_OK,i); + finish(); + return; + } + initiate(fragen.get(currentFrage)); + } + + +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/activities/fragenActivities/TextFrageActivity.java b/app/src/main/java/com/bernard/mcqinator/view/activities/fragenActivities/TextFrageActivity.java new file mode 100644 index 0000000..4fa1339 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/activities/fragenActivities/TextFrageActivity.java @@ -0,0 +1,45 @@ +package com.bernard.mcqinator.view.activities.fragenActivities; + +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.frageable.TextFrageable; +import com.bernard.mcqinator.api.fragen.TextFrage; + +public class TextFrageActivity extends FrageActivity implements View.OnClickListener{ + + TextView frage; + EditText antwort; + Button nextButton; + + @Override + public void initiate(TextFrage f) { + frage.setText(f.getFrage()); + } + + @Override + @SuppressWarnings("null") + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_text_frage); + + TextFrageable.TextData data = getIntent().getParcelableExtra(QUIZ_DATA); + + frage= findViewById(R.id.frageText); + antwort = findViewById(R.id.antwortText); + nextButton = findViewById(R.id.next); + + nextButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + TextFrage.Reply reply = new TextFrage.Reply(antwort.getText().toString()); + + reply(reply); + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/quizEditing/formQuiz/EditFormQuiz.java b/app/src/main/java/com/bernard/mcqinator/view/quizEditing/formQuiz/EditFormQuiz.java new file mode 100644 index 0000000..a85b00a --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/quizEditing/formQuiz/EditFormQuiz.java @@ -0,0 +1,112 @@ +package com.bernard.mcqinator.view.quizEditing.formQuiz; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ListView; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.QuizMetadata; +import com.bernard.mcqinator.api.quiz.FormQuiz; +import com.bernard.mcqinator.api.quiz.Quiz; +import com.bernard.mcqinator.controller.FileManager; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.view.activities.ChoosingQuizToEditActivity; + +import java.io.IOException; + +public class EditFormQuiz extends ListActivity { + + public static final int ITEM_NEW = 0x5603B6BD; + public static final int ITEM_CHANGE = 0x68E0B3A1; + + QuizMetadata quizMetadata; + FormQuiz quiz; + FormQuiz.ItemAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent i = getIntent(); + long fileID = i.getLongExtra(ChoosingQuizToEditActivity.QUIZ_FILEID,-1);//TODO check intent + Quiz q; + try { + quizMetadata = QuizMetadataManager.getQuiz(this,fileID); + q = FileManager.getQuiz(quizMetadata,this); + if(q == null)throw new IOException("FileManager.getQuiz returned null"); + } catch (IOException e) { + Log.e("EditingFormQuiz","Cannot instantiate the quiz or the quizmetadata",e); + finish(); + return; + } + quiz = (FormQuiz)q; + setContentView(quiz.getEditActivityLayoutID()); + try { + Button neueItem = findViewById(R.id.neueItemButton);//TODO check button presence (not a try / catch) + neueItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(EditFormQuiz.this, EditFormQuizItem.class); + i.putExtra(EditFormQuizItem.TO_EDIT_ITEM, (Parcelable) null); + i.putExtra(EditFormQuizItem.QUIZ_FILEID, quizMetadata.getFileID()); + startActivityForResult(i, ITEM_NEW); + } + }); + }catch(NullPointerException e){ + Log.e("EditFormQuiz","The layout "+quiz.getClass().getName()+".getEditActivityLayoutID() has to own a Button with the ID 'neueItemButton'"); + }catch(ClassCastException e){ + Log.e("EditFormQuiz","The view 'neueItemButton' in the layout "+quiz.getClass().getName()+".getEditActivityLayoutID() has to be a Button"); + } + + try { + Button doneButton = findViewById(R.id.doneButton);//TODO check button presence (not a try / catch) + doneButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FileManager.saveQuiz(quizMetadata,quiz,EditFormQuiz.this); + finish(); + } + }); + }catch(NullPointerException e){ + Log.d("EditFormQuiz","The layout "+quiz.getClass().getName()+".getEditActivityLayoutID() has to own a Button with the ID 'doneButton'"); + }catch(ClassCastException e){ + Log.d("EditFormQuiz","The view 'doneButton' in the layout "+quiz.getClass().getName()+".getEditActivityLayoutID() has to be a Button"); + } + + adapter = quiz.new ItemAdapter(this); + setListAdapter(adapter); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if(requestCode == ITEM_NEW && resultCode == RESULT_OK){ + FormQuiz.Item v = data.getParcelableExtra(EditFormQuizItem.EDITED_ITEM); + quiz.addItem(v); + FileManager.saveQuiz(quizMetadata,quiz,this); + adapter = quiz.new ItemAdapter(this); + setListAdapter(adapter); + } + if(requestCode == ITEM_CHANGE && resultCode == RESULT_OK){ + FormQuiz.Item v = data.getParcelableExtra(EditFormQuizItem.EDITED_ITEM); + int pos = data.getIntExtra(EditFormQuizItem.ITEM_POSITION,0); + quiz.setItem(pos,v); + FileManager.saveQuiz(quizMetadata,quiz,this); + adapter = quiz.new ItemAdapter(this); + setListAdapter(adapter); + } + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + Intent i = new Intent(EditFormQuiz.this, EditFormQuizItem.class); + i.putExtra(EditFormQuizItem.TO_EDIT_ITEM,(Parcelable)adapter.getItem(position)); + i.putExtra(EditFormQuizItem.ITEM_POSITION,position); + i.putExtra(EditFormQuizItem.QUIZ_FILEID,quizMetadata.getFileID()); + startActivityForResult(i,ITEM_CHANGE); + } +} diff --git a/app/src/main/java/com/bernard/mcqinator/view/quizEditing/formQuiz/EditFormQuizItem.java b/app/src/main/java/com/bernard/mcqinator/view/quizEditing/formQuiz/EditFormQuizItem.java new file mode 100644 index 0000000..d2b7d91 --- /dev/null +++ b/app/src/main/java/com/bernard/mcqinator/view/quizEditing/formQuiz/EditFormQuizItem.java @@ -0,0 +1,162 @@ +package com.bernard.mcqinator.view.quizEditing.formQuiz; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.bernard.mcqinator.R; +import com.bernard.mcqinator.api.quiz.FormQuiz; +import com.bernard.mcqinator.controller.FileManager; +import com.bernard.mcqinator.controller.QuizMetadataManager; +import com.bernard.mcqinator.inutil.ArrayInutil; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class EditFormQuizItem extends Activity implements View.OnClickListener{ + + public static final String EDITED_ITEM = "com.bernard.qcminator.EDITED_ITEM"; + public static final String QUIZ_FILEID = "com.bernard.qcminator.QUIZ_FILEID"; + public static final String TO_EDIT_ITEM = "com.bernard.qcminator.TO_EDIT_ITEM"; + public static final String ITEM_POSITION = "com.bernard.qcminator.ITEM_POSITION"; + + View[] addButtons; + ViewGroup[] formsLayout; + ViewGroup itemLayout; + List falseFields; + FormQuiz quiz; + + final View.OnClickListener delFalshItemOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + //TODO ask confirmation if editText not empty + //TODO check cast + int pos = (int)v.getTag(R.id.itemLayout); + ((ViewGroup)falseFields.get(pos).getParent()).removeView(falseFields.get(pos)); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + quiz = (FormQuiz) FileManager.getQuiz(QuizMetadataManager.getQuiz(this,getIntent().getLongExtra(QUIZ_FILEID,0)),this); + if(quiz == null)throw new IOException("FileManager.getQuiz returned null ..."); + } catch (IOException e) { + e.printStackTrace();//TODO customize error log + return; + } + + setContentView(quiz.getEditItemActivityLayoutID()); + + final FormQuiz.Item item = (getIntent().getParcelableExtra(TO_EDIT_ITEM)!=null)?(FormQuiz.Item)getIntent().getParcelableExtra(TO_EDIT_ITEM):new FormQuiz.Item(new FormQuiz.Item.Form[quiz.getFormCount()]); + + formsLayout = new LinearLayout[item.getForms().length]; + addButtons = new View[item.getForms().length]; + falseFields = new ArrayList<>(); + + itemLayout = findViewById(R.id.itemLayout); + //TODO add exception if ids missing + for (int i = 0; i < item.getForms().length; i++) { + formsLayout[i] = (ViewGroup)getLayoutInflater().inflate(quiz.getEditItemActivityFormLayoutID(),itemLayout,false); + TextView richtig = formsLayout[i].findViewById(R.id.realItem); + richtig.setText(item.getForms()[i].getRichtig()); + + TextView formName = formsLayout[i].findViewById(R.id.formName); + formName.setText(quiz.getFormNameStringId(i)); + + View addItemButton = formsLayout[i].findViewById(R.id.addButton); + addItemButton.setOnClickListener(this); + addButtons[i] = addItemButton; + itemLayout.addView(formsLayout[i]); + + for (int j = 0; j < item.getForms()[i].getFalshe().length; j++) { + View falshe = getLayoutInflater().inflate(R.layout.edit_form_quiz_item_false,formsLayout[i],false); + TextView falseField = falshe.findViewById(R.id.falshItem); + falseField.setTag(R.id.falshItem,i); + falseField.setText(item.getForms()[i].getFalshe()[j]); + View delFalseButton = falshe.findViewById(R.id.delFalshItemButton); + delFalseButton.setTag(R.id.itemLayout,falseFields.size()); + delFalseButton.setOnClickListener(delFalshItemOnClickListener); + formsLayout[i].addView(falshe); + } + } + + Button doneButton = findViewById(R.id.doneButton);//TODO check exists + doneButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Resources r = EditFormQuizItem.this.getResources(); + FormQuiz.Item.Form[] forms = new FormQuiz.Item.Form[formsLayout.length]; + for (int i = 0; i < item.getForms().length; i++) { + ArrayList falshe = new ArrayList<>(); + String realItem = ((TextView)formsLayout[i].findViewById(R.id.realItem)).getText().toString(); + if(realItem.isEmpty()){ + String e = r.getString(R.string.missingReal,r.getString(quiz.getFormNameStringId(i))); + Log.i("ParsingResults", e); + Toast.makeText(EditFormQuizItem.this,e,Toast.LENGTH_SHORT).show(); + return; + } + List childs = getAllChildren(formsLayout[i]); + for (View child : childs) { + if(child.getTag(R.id.falshItem) == null) + continue; + String s = ((TextView)child).getText().toString(); + if(!s.isEmpty()) falshe.add(s); + } + Log.v("Configurator","FalsheAntwort : " + falshe); + String[] falsharray = new String[falshe.size()]; + falshe.toArray(falsharray); + forms[i] = new FormQuiz.Item.Form((byte)0/* TODO implement formID */,realItem,falsharray); + } + + FormQuiz.Item newItem = new FormQuiz.Item(forms); + Intent i = new Intent(); + + i.putExtra(EDITED_ITEM,(Parcelable)newItem); + + setResult(RESULT_OK,i); + finish(); + } + }); + } + + public static List getAllChildren(View v){ + return getAllChildren(v,new ArrayList()); + } + + private static List getAllChildren(View v,List l){ + l.add(v); + if(v instanceof ViewGroup){ + ViewGroup vg = (ViewGroup)v; + for (int i = 0; i < vg.getChildCount(); i++) + getAllChildren(vg.getChildAt(i),l); + } + return l; + } + + @Override + public void onClick(View v) { + int buttonPos = ArrayInutil.indexOf(addButtons,v); + + View falshe = getLayoutInflater().inflate(R.layout.edit_form_quiz_item_false,formsLayout[buttonPos],false); + TextView falseField = falshe.findViewById(R.id.falshItem); + View delFalseButton = falshe.findViewById(R.id.delFalshItemButton); + falseField.setTag(R.id.falshItem,buttonPos); + delFalseButton.setTag(R.id.itemLayout,falseFields.size()); + delFalseButton.setOnClickListener(delFalshItemOnClickListener); + falseFields.add(falshe); + formsLayout[buttonPos].addView(falshe); + } +} diff --git a/app/src/main/res/drawable-hdpi/bloc_quiz_icon.png b/app/src/main/res/drawable-hdpi/bloc_quiz_icon.png new file mode 100644 index 0000000..fa13c91 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bloc_quiz_icon.png differ diff --git a/app/src/main/res/drawable-hdpi/dark_tiled_background.png b/app/src/main/res/drawable-hdpi/dark_tiled_background.png new file mode 100644 index 0000000..484f928 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/dark_tiled_background.png differ diff --git a/app/src/main/res/drawable-hdpi/de_verben_quiz.png b/app/src/main/res/drawable-hdpi/de_verben_quiz.png new file mode 100644 index 0000000..9d7f428 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/de_verben_quiz.png differ diff --git a/app/src/main/res/drawable-hdpi/default_quiz_icon.png b/app/src/main/res/drawable-hdpi/default_quiz_icon.png new file mode 100644 index 0000000..59e3c03 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/default_quiz_icon.png differ diff --git a/app/src/main/res/drawable-hdpi/light_tiled_background.png b/app/src/main/res/drawable-hdpi/light_tiled_background.png new file mode 100644 index 0000000..8950555 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/light_tiled_background.png differ diff --git a/app/src/main/res/drawable-hdpi/white_button_default.9.png b/app/src/main/res/drawable-hdpi/white_button_default.9.png new file mode 100644 index 0000000..451cde2 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/white_button_default.9.png differ diff --git a/app/src/main/res/drawable-hdpi/white_button_disabled.9.png b/app/src/main/res/drawable-hdpi/white_button_disabled.9.png new file mode 100644 index 0000000..eeff9b4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/white_button_disabled.9.png differ diff --git a/app/src/main/res/drawable-hdpi/white_button_focused.9.png b/app/src/main/res/drawable-hdpi/white_button_focused.9.png new file mode 100644 index 0000000..bf68a6b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/white_button_focused.9.png differ diff --git a/app/src/main/res/drawable-hdpi/white_button_pressed.9.png b/app/src/main/res/drawable-hdpi/white_button_pressed.9.png new file mode 100644 index 0000000..1994318 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/white_button_pressed.9.png differ diff --git a/app/src/main/res/drawable-mdpi/bloc_quiz_icon.png b/app/src/main/res/drawable-mdpi/bloc_quiz_icon.png new file mode 100644 index 0000000..fd8c62c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/bloc_quiz_icon.png differ diff --git a/app/src/main/res/drawable-mdpi/dark_tiled_background.png b/app/src/main/res/drawable-mdpi/dark_tiled_background.png new file mode 100644 index 0000000..965f8a2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/dark_tiled_background.png differ diff --git a/app/src/main/res/drawable-mdpi/de_verben_quiz.png b/app/src/main/res/drawable-mdpi/de_verben_quiz.png new file mode 100644 index 0000000..3fd66da Binary files /dev/null and b/app/src/main/res/drawable-mdpi/de_verben_quiz.png differ diff --git a/app/src/main/res/drawable-mdpi/default_quiz_icon.png b/app/src/main/res/drawable-mdpi/default_quiz_icon.png new file mode 100644 index 0000000..ef57556 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/default_quiz_icon.png differ diff --git a/app/src/main/res/drawable-mdpi/light_tiled_background.png b/app/src/main/res/drawable-mdpi/light_tiled_background.png new file mode 100644 index 0000000..a9b1be0 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/light_tiled_background.png differ diff --git a/app/src/main/res/drawable-mdpi/white_button_default.9.png b/app/src/main/res/drawable-mdpi/white_button_default.9.png new file mode 100644 index 0000000..3b9c937 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/white_button_default.9.png differ diff --git a/app/src/main/res/drawable-mdpi/white_button_disabled.9.png b/app/src/main/res/drawable-mdpi/white_button_disabled.9.png new file mode 100644 index 0000000..ae25c90 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/white_button_disabled.9.png differ diff --git a/app/src/main/res/drawable-mdpi/white_button_focused.9.png b/app/src/main/res/drawable-mdpi/white_button_focused.9.png new file mode 100644 index 0000000..9ba1fb7 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/white_button_focused.9.png differ diff --git a/app/src/main/res/drawable-mdpi/white_button_pressed.9.png b/app/src/main/res/drawable-mdpi/white_button_pressed.9.png new file mode 100644 index 0000000..835c9be Binary files /dev/null and b/app/src/main/res/drawable-mdpi/white_button_pressed.9.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-xhdpi/bloc_quiz_icon.png b/app/src/main/res/drawable-xhdpi/bloc_quiz_icon.png new file mode 100644 index 0000000..0098676 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/bloc_quiz_icon.png differ diff --git a/app/src/main/res/drawable-xhdpi/dark_tiled_background.png b/app/src/main/res/drawable-xhdpi/dark_tiled_background.png new file mode 100644 index 0000000..773c2fb Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/dark_tiled_background.png differ diff --git a/app/src/main/res/drawable-xhdpi/de_verben_quiz.png b/app/src/main/res/drawable-xhdpi/de_verben_quiz.png new file mode 100644 index 0000000..4ed1378 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/de_verben_quiz.png differ diff --git a/app/src/main/res/drawable-xhdpi/default_quiz_icon.png b/app/src/main/res/drawable-xhdpi/default_quiz_icon.png new file mode 100644 index 0000000..98f16f5 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/default_quiz_icon.png differ diff --git a/app/src/main/res/drawable-xhdpi/light_tiled_background.png b/app/src/main/res/drawable-xhdpi/light_tiled_background.png new file mode 100644 index 0000000..4e4351f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/light_tiled_background.png differ diff --git a/app/src/main/res/drawable-xhdpi/white_button_default.9.png b/app/src/main/res/drawable-xhdpi/white_button_default.9.png new file mode 100644 index 0000000..1e5207f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/white_button_default.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/white_button_disabled.9.png b/app/src/main/res/drawable-xhdpi/white_button_disabled.9.png new file mode 100644 index 0000000..ca30e59 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/white_button_disabled.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/white_button_focused.9.png b/app/src/main/res/drawable-xhdpi/white_button_focused.9.png new file mode 100644 index 0000000..8c63e35 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/white_button_focused.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/white_button_pressed.9.png b/app/src/main/res/drawable-xhdpi/white_button_pressed.9.png new file mode 100644 index 0000000..0ee5ac0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/white_button_pressed.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bloc_quiz_icon.png b/app/src/main/res/drawable-xxhdpi/bloc_quiz_icon.png new file mode 100644 index 0000000..0068d22 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bloc_quiz_icon.png differ diff --git a/app/src/main/res/drawable-xxhdpi/dark_tiled_background.png b/app/src/main/res/drawable-xxhdpi/dark_tiled_background.png new file mode 100644 index 0000000..e5caaf4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/dark_tiled_background.png differ diff --git a/app/src/main/res/drawable-xxhdpi/de_verben_quiz.png b/app/src/main/res/drawable-xxhdpi/de_verben_quiz.png new file mode 100644 index 0000000..e84f3d0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/de_verben_quiz.png differ diff --git a/app/src/main/res/drawable-xxhdpi/default_quiz_icon.png b/app/src/main/res/drawable-xxhdpi/default_quiz_icon.png new file mode 100644 index 0000000..9d8e2cf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/default_quiz_icon.png differ diff --git a/app/src/main/res/drawable-xxhdpi/light_tiled_background.png b/app/src/main/res/drawable-xxhdpi/light_tiled_background.png new file mode 100644 index 0000000..953a82b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/light_tiled_background.png differ diff --git a/app/src/main/res/drawable-xxhdpi/white_button_default.9.png b/app/src/main/res/drawable-xxhdpi/white_button_default.9.png new file mode 100644 index 0000000..8cf09fa Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/white_button_default.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/white_button_disabled.9.png b/app/src/main/res/drawable-xxhdpi/white_button_disabled.9.png new file mode 100644 index 0000000..eac5650 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/white_button_disabled.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/white_button_focused.9.png b/app/src/main/res/drawable-xxhdpi/white_button_focused.9.png new file mode 100644 index 0000000..ce95730 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/white_button_focused.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/white_button_pressed.9.png b/app/src/main/res/drawable-xxhdpi/white_button_pressed.9.png new file mode 100644 index 0000000..c0baf79 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/white_button_pressed.9.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bloc_quiz_icon.png b/app/src/main/res/drawable-xxxhdpi/bloc_quiz_icon.png new file mode 100644 index 0000000..6927b89 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bloc_quiz_icon.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/dark_tiled_background.png b/app/src/main/res/drawable-xxxhdpi/dark_tiled_background.png new file mode 100644 index 0000000..ae1ac8b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/dark_tiled_background.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/de_verben_quiz.png b/app/src/main/res/drawable-xxxhdpi/de_verben_quiz.png new file mode 100644 index 0000000..eda7e7d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/de_verben_quiz.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/light_tiled_background.png b/app/src/main/res/drawable-xxxhdpi/light_tiled_background.png new file mode 100644 index 0000000..b685262 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/light_tiled_background.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/white_button_default.9.png b/app/src/main/res/drawable-xxxhdpi/white_button_default.9.png new file mode 100644 index 0000000..6829359 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/white_button_default.9.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/white_button_disabled.9.png b/app/src/main/res/drawable-xxxhdpi/white_button_disabled.9.png new file mode 100644 index 0000000..02a691d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/white_button_disabled.9.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/white_button_focused.9.png b/app/src/main/res/drawable-xxxhdpi/white_button_focused.9.png new file mode 100644 index 0000000..6006cb5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/white_button_focused.9.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/white_button_pressed.9.png b/app/src/main/res/drawable-xxxhdpi/white_button_pressed.9.png new file mode 100644 index 0000000..4d14074 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/white_button_pressed.9.png differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/white_button.xml b/app/src/main/res/drawable/white_button.xml new file mode 100644 index 0000000..b5c8eed --- /dev/null +++ b/app/src/main/res/drawable/white_button.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_bloc_frage.xml b/app/src/main/res/layout/activity_bloc_frage.xml new file mode 100644 index 0000000..229dcb8 --- /dev/null +++ b/app/src/main/res/layout/activity_bloc_frage.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/activity_choosing_quiz.xml b/app/src/main/res/layout/activity_choosing_quiz.xml new file mode 100644 index 0000000..17ee1b9 --- /dev/null +++ b/app/src/main/res/layout/activity_choosing_quiz.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_choosing_quiz_to_edit.xml b/app/src/main/res/layout/activity_choosing_quiz_to_edit.xml new file mode 100644 index 0000000..98369a3 --- /dev/null +++ b/app/src/main/res/layout/activity_choosing_quiz_to_edit.xml @@ -0,0 +1,36 @@ + + +