From c0b392be7c6e532ffc973a8452c96637f2dfef87 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Wed, 10 Oct 2018 18:29:12 -0400 Subject: [PATCH 01/12] Added ability to undo recent actions Merged allNames and scoutNames, and made them use Scout lists instead of String lists. --- .../ca/scoutingserverapp/Action.java | 28 +++ .../ca/scoutingserverapp/MainActivity.java | 163 ++++++++++++------ .../ca/scoutingserverapp/Scout.java | 8 + app/src/main/res/layout/name_submitter.xml | 7 + 4 files changed, 156 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/lakeeffect/ca/scoutingserverapp/Action.java diff --git a/app/src/main/java/lakeeffect/ca/scoutingserverapp/Action.java b/app/src/main/java/lakeeffect/ca/scoutingserverapp/Action.java new file mode 100644 index 0000000..818c629 --- /dev/null +++ b/app/src/main/java/lakeeffect/ca/scoutingserverapp/Action.java @@ -0,0 +1,28 @@ +package lakeeffect.ca.scoutingserverapp; + +import android.view.View; + +/** + * An action that happens (someone adds a scout, changes properties, etc.) + * + * This is used to undo that action. The actions are stored in a list to be recalled upon if an undo is necessary + */ +public class Action { + //0: start match added + //1: last match added + //2: added scout + //3: removed scout + int type; + + //the scout that this happened to + Scout scout; + + //the view that was modified, added or removed + View view; + + public Action(int type, Scout scout, View view) { + this.type = type; + this.scout = scout; + this.view = view; + } +} diff --git a/app/src/main/java/lakeeffect/ca/scoutingserverapp/MainActivity.java b/app/src/main/java/lakeeffect/ca/scoutingserverapp/MainActivity.java index 3ab7fdf..a398618 100644 --- a/app/src/main/java/lakeeffect/ca/scoutingserverapp/MainActivity.java +++ b/app/src/main/java/lakeeffect/ca/scoutingserverapp/MainActivity.java @@ -69,13 +69,14 @@ public class MainActivity extends AppCompatActivity { //the robots schedules ArrayList> robotSchedule = new ArrayList<>(); - //list of allNames added - ArrayList allNames = new ArrayList<>(); - //the names that have been checked off - ArrayList selectedNames = new ArrayList<>(); + //the list of all scouts + ArrayList allScouts = new ArrayList<>(); //for each selected name, there is an array of assigned robots (0 - 5 per match, -1 being a break) ArrayList assignedRobot = new ArrayList<>(); + //the last actions that have happened, used to undo actions is necessary + ArrayList pastActions = new ArrayList<>(); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -236,18 +237,13 @@ public void onClick(View v) { //load names SharedPreferences namesPreferences = getSharedPreferences("names", MODE_PRIVATE); - String allNamesText = namesPreferences.getString("allNames", ""); - if (!allNamesText.equals("")) { - allNames = new ArrayList<>(Arrays.asList(allNamesText.split(","))); - } - - String selectedNamesText = namesPreferences.getString("selectedNames", ""); + String selectedNamesText = namesPreferences.getString("allScouts", ""); if (!selectedNamesText.equals("")) { String[] selectedNamesArray = selectedNamesText.split(","); for (int i = 0; i < selectedNamesArray.length; i++) { Scout scout = new Scout(selectedNamesArray[i]); - selectedNames.add(scout); + allScouts.add(scout); } } @@ -259,7 +255,7 @@ public void onClick(View v) { //add all start matches String[] scoutStartMatchesArray = selectedNameStartMatchesArray[i].split(";"); for (int s = 0; s < scoutStartMatchesArray.length; s++) { - selectedNames.get(i).startMatches.add(Integer.parseInt(scoutStartMatchesArray[s])); + allScouts.get(i).startMatches.add(Integer.parseInt(scoutStartMatchesArray[s])); } } } @@ -273,7 +269,7 @@ public void onClick(View v) { //add all last matches String[] scoutLastMatchesArray = selectedNameLastMatchesArray[i].split(";"); for (int s = 0; s < scoutLastMatchesArray.length; s++) { - selectedNames.get(i).lastMatches.add(Integer.parseInt(scoutLastMatchesArray[s])); + allScouts.get(i).lastMatches.add(Integer.parseInt(scoutLastMatchesArray[s])); } } } @@ -469,7 +465,7 @@ public void readSchedule() throws IOException { //creates the schedule based on the selected usernames //returns null if successful, and error message if not public String createSchedule() { - if (selectedNames.size() < 6) { + if (allScouts.size() < 6) { return "You must select at least 6 scouts"; } @@ -479,7 +475,7 @@ public String createSchedule() { //set assigned robots to correct size assignedRobot = new ArrayList<>(); - for (int i = 0; i < selectedNames.size(); i++) { + for (int i = 0; i < allScouts.size(); i++) { assignedRobot.add(new int[robotSchedule.size()]); } @@ -499,10 +495,10 @@ public String createSchedule() { } //if they are not starting on the first match - if (selectedNames.get(i).getLowestStartMatch() > 0) { - Scout scout = selectedNames.get(i); - selectedNames.remove(scout); - selectedNames.add(scout); + if (allScouts.get(i).getLowestStartMatch() > 0) { + Scout scout = allScouts.get(i); + allScouts.remove(scout); + allScouts.add(scout); //try again i--; //add to the non ready scouts to make sure this is not an infinite loop @@ -510,25 +506,25 @@ public String createSchedule() { continue; } - scoutsOn[i] = new Scout(i, selectedNames.get(i).name); + scoutsOn[i] = new Scout(i, allScouts.get(i).name); } - for (int i = 6; i < selectedNames.size(); i++) { + for (int i = 6; i < allScouts.size(); i++) { //if they are not starting on the first match - if (selectedNames.get(i).getLowestStartMatch() > 0) { + if (allScouts.get(i).getLowestStartMatch() > 0) { continue; } - Scout scout = new Scout(i, selectedNames.get(i).name); + Scout scout = new Scout(i, allScouts.get(i).name); scoutsOff.add(scout); scout.timeOff = 0; } for (int matchNum = 0; matchNum < robotSchedule.size(); matchNum++) { //figure out if some selected names should be added or removed because it is now their start match or last match - for (int i = 0; i < selectedNames.size(); i++) { + for (int i = 0; i < allScouts.size(); i++) { //if it is time to add this scout to the roster and they are not already added - Scout scout = selectedNames.get(i); + Scout scout = allScouts.get(i); boolean existsAtMatch = scout.existsAtMatch(matchNum); if (existsAtMatch && getScout(i, scoutsOff) == -1 && getScout(i, scoutsOn) == -1) { Scout newScout = new Scout(i, scout.name); @@ -660,18 +656,17 @@ public void openNameEditor() { final TextView currentMatchNumber = ((TextView) addName.findViewById(R.id.currentMatchNumber)); //add checkbox and close button for each username - for (int i = 0; i < allNames.size(); i++) { + for (int i = 0; i < allScouts.size(); i++) { final View view = View.inflate(this, R.layout.closable_checkbox, null); CheckBox checkBox = ((CheckBox) view.findViewById(R.id.nameCheckBox)); - final String name = allNames.get(i); + final Scout scout = allScouts.get(i); - checkBox.setText(name); + checkBox.setText(scout.name); //if it is selected, check the box - int index = getScout(name, selectedNames); - if (index != -1 && selectedNames.get(index).existsAtMatch(robotSchedule.size() - 1)) { + if (scout.existsAtMatch(robotSchedule.size() - 1)) { checkBox.setChecked(true); } @@ -679,7 +674,7 @@ public void openNameEditor() { checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - nameClicked(buttonView, isChecked, name, currentMatchNumber); + nameClicked(buttonView, isChecked, scout.name, currentMatchNumber); } }); @@ -689,10 +684,11 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @Override public void onClick(View v) { createdNames.removeView(view); - allNames.remove(name); - if (getScout(name, selectedNames) != -1) { - selectedNames.remove(getScout(name, selectedNames)); - } + allScouts.remove(scout); + + //a scout was removed + //add the action to the past actions list + pastActions.add(new Action(3, scout, view)); updateNames(); } @@ -715,13 +711,17 @@ public void onClick(View v) { nameEditText.setText(""); //add it to the list - allNames.add(name); + final Scout scout = new Scout(-1, name); + allScouts.add(scout); updateNames(); //add it to the UI final View view = View.inflate(MainActivity.this, R.layout.closable_checkbox, null); + //add the action to the past actions list + pastActions.add(new Action(2, scout, view)); + CheckBox checkBox = ((CheckBox) view.findViewById(R.id.nameCheckBox)); checkBox.setText(name); @@ -739,10 +739,11 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @Override public void onClick(View v) { createdNames.removeView(view); - allNames.remove(name); - if (getScout(name, selectedNames) != -1) { - selectedNames.remove(getScout(name, selectedNames)); - } + allScouts.remove(scout); + + //a scout was removed + //add the action to the past actions list + pastActions.add(new Action(3, scout, view)); updateNames(); } @@ -752,6 +753,48 @@ public void onClick(View v) { } }); + //setup undo button + addName.findViewById(R.id.undoPastAction).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //create confirmation dialog + new AlertDialog.Builder(MainActivity.this) + .setTitle("Are you sure you would like to undo?") + .setNegativeButton("No", null) + .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + //get last action + Action undoAction = pastActions.get(pastActions.size() - 1); + + switch (undoAction.type) { + case 0: + undoAction.scout.startMatches.remove(undoAction.scout.startMatches.size() - 1); + undoAction.scout.undo = true; + ((CheckBox) undoAction.view).setChecked(false); + break; + case 1: + undoAction.scout.lastMatches.remove(undoAction.scout.lastMatches.size() - 1); + undoAction.scout.undo = true; + ((CheckBox) undoAction.view).setChecked(true); + break; + case 2: + allScouts.remove(undoAction.scout); + createdNames.removeView(undoAction.view); + break; + case 3: + allScouts.add(undoAction.scout); + createdNames.addView(undoAction.view); + } + + //remove past action from list now + pastActions.remove(undoAction); + } + }) + .show(); + } + }); + fullView.addView(addName); fullScrollView.addView(fullView); @@ -766,14 +809,26 @@ public void onClick(View v) { //called when a name is checked or unchecked public void nameClicked(CompoundButton checkbox, boolean isChecked, String name, TextView currentMatchNumber) { - int scoutIndex = getScout(name, selectedNames); + int scoutIndex = getScout(name, allScouts); int matchNum = Integer.parseInt(currentMatchNumber.getText().toString()) - 1; + //the checkbox was programmatically checked, no need to check anything + if (scoutIndex != -1 && allScouts.get(scoutIndex).undo) { + allScouts.get(scoutIndex).undo = false; + return; + } + if (isChecked) { if (scoutIndex == -1) { - selectedNames.add(new Scout(name, matchNum)); + allScouts.add(new Scout(name, matchNum)); + + runOnUiThread(new Thread() { + public void run() { + Toast.makeText(MainActivity.this, "This should not happen!!!", Toast.LENGTH_SHORT).show(); + } + }); } else { - Scout scout = selectedNames.get(scoutIndex); + Scout scout = allScouts.get(scoutIndex); if (scout.existsAtMatch(matchNum)) { //this action should not happen, this is an invalid time @@ -788,10 +843,13 @@ public void run() { //enable them at that match number scout.startMatches.add(matchNum); + + //add the action to the past actions list + pastActions.add(new Action(0, scout, checkbox)); } } else { if (scoutIndex != -1) { - Scout scout = selectedNames.get(scoutIndex); + Scout scout = allScouts.get(scoutIndex); if (!scout.existsAtMatch(matchNum)) { //this action should not happen, this is an invalid time @@ -817,6 +875,11 @@ public void run() { //undo as an error has been caused scout.lastMatches.remove(scout.lastMatches.size() - 1); checkbox.setChecked(true); + } else { + //it was successful + + //add the action to the past actions list + pastActions.add(new Action(1, scout, checkbox)); } } } @@ -830,9 +893,9 @@ public void updateNames() { //get all the names in csv String names = ""; - for (String name : allNames) { - names += name; - if (allNames.indexOf(name) < allNames.size() - 1) { + for (Scout scout : allScouts) { + names += scout.name; + if (allScouts.indexOf(scout) < allScouts.size() - 1) { names += ","; } } @@ -844,7 +907,7 @@ public void updateNames() { String allSelectedNames = ""; String allSelectedNameStartMatches = ""; String allSelectedNameLastMatches = ""; - for (Scout scout : selectedNames) { + for (Scout scout : allScouts) { allSelectedNames += scout.name; for (int i = 0; i < scout.startMatches.size(); i++) { allSelectedNameStartMatches += scout.startMatches.get(i); @@ -859,13 +922,13 @@ public void updateNames() { } } - if (selectedNames.indexOf(scout) < selectedNames.size() - 1) { + if (allScouts.indexOf(scout) < allScouts.size() - 1) { allSelectedNames += ","; allSelectedNameStartMatches += ","; allSelectedNameLastMatches += ","; } } - editor.putString("selectedNames", allSelectedNames); + editor.putString("allScouts", allSelectedNames); editor.putString("selectedNameStartMatches", allSelectedNameStartMatches); editor.putString("selectedNameLastMatches", allSelectedNameLastMatches); diff --git a/app/src/main/java/lakeeffect/ca/scoutingserverapp/Scout.java b/app/src/main/java/lakeeffect/ca/scoutingserverapp/Scout.java index 3731257..739a199 100644 --- a/app/src/main/java/lakeeffect/ca/scoutingserverapp/Scout.java +++ b/app/src/main/java/lakeeffect/ca/scoutingserverapp/Scout.java @@ -22,6 +22,9 @@ public class Scout { //inclusive ArrayList lastMatches = new ArrayList<>(); + //if this just had an undo applied, used to ignore onCheckChange listeners + boolean undo; + public Scout(int id, String name) { this.id = id; this.name = name; @@ -55,6 +58,11 @@ public int getLowestStartMatch() { //does this scout exist at this match public boolean existsAtMatch(int matchNum) { + //they were never selected if this is true + if (startMatches.size() == 0) { + return false; + } + for (int i = 0; i < startMatches.size(); i++) { if (startMatches.get(i) <= matchNum) { //has it been closed since then diff --git a/app/src/main/res/layout/name_submitter.xml b/app/src/main/res/layout/name_submitter.xml index 77d1c7a..497eb19 100644 --- a/app/src/main/res/layout/name_submitter.xml +++ b/app/src/main/res/layout/name_submitter.xml @@ -34,4 +34,11 @@ android:layout_gravity="center_horizontal" android:text="1" /> +