From d2f92111ee6fa89f1591448d9b4ac64dc206f96b Mon Sep 17 00:00:00 2001 From: Gerald Birngruber Date: Wed, 29 Apr 2020 11:13:11 +0200 Subject: [PATCH 1/4] Games_006 Testcases for GUI and Wordlist [BG, SS] --- .../com/swt20/swt_morning2/HangmanTests.java | 84 +++++++++++++++++++ .../com/swt20/swt_morning2/WordListTest.java | 45 ++++++++++ 2 files changed, 129 insertions(+) create mode 100644 app/src/androidTest/java/com/swt20/swt_morning2/HangmanTests.java create mode 100644 app/src/test/java/com/swt20/swt_morning2/WordListTest.java diff --git a/app/src/androidTest/java/com/swt20/swt_morning2/HangmanTests.java b/app/src/androidTest/java/com/swt20/swt_morning2/HangmanTests.java new file mode 100644 index 0000000..28d67f7 --- /dev/null +++ b/app/src/androidTest/java/com/swt20/swt_morning2/HangmanTests.java @@ -0,0 +1,84 @@ +package com.swt20.swt_morning2; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.rule.ActivityTestRule; + +import junit.framework.AssertionFailedError; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static androidx.test.espresso.Espresso.onView; + +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.action.ViewActions.typeText; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +@RunWith(AndroidJUnit4.class) +public class HangmanTests { + + @Rule + public ActivityTestRule activityRule + = new ActivityTestRule<>(MainActivity.class); + + @Test + public void PlayGame() { + Activity activity = activityRule.getActivity(); + ScoreTracker st = new ScoreTracker(activity.getApplicationContext()); + int old_score = st.getScore(Game.HANGMAN); + // Go from Main Menu to Hangman Menu + onView(withId(R.id.hangmanButton)).perform(click()); + + // Go from Hangman Menu to Game + onView(withId(R.id.ttt_menu_button)).perform(click()); + String[] letters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}; + for (String str : letters) { + onView(withId(R.id.plainText_nextChar)).perform(typeText(str)); + try { + onView(withId(R.id.button_playagain)).check(matches(isDisplayed())); + // View displayed + assert (st.getScore(Game.HANGMAN) == old_score+1); + return; + } catch (AssertionFailedError e) { + // View not displayed + } + } + assert (false); + } + + @Test + public void PlayGameTwice() { + + // Go from Main Menu to Hangman Menu + onView(withId(R.id.hangmanButton)).perform(click()); + + // Go from Hangman Menu to Game + onView(withId(R.id.ttt_menu_button)).perform(click()); + + String[] letters = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}; + for(int i = 0; i < 2;i++) { + for (String str : letters) { + onView(withId(R.id.plainText_nextChar)).perform(typeText(str)); + try { + onView(withId(R.id.button_playagain)).check(matches(isDisplayed())); + // View displayed + if(i < 1) { + onView(withId(R.id.button_playagain)).perform(click()); + } else { + return; + } + } catch (AssertionFailedError e) { + // View not displayed + } + } + } + assert (false); + } +} diff --git a/app/src/test/java/com/swt20/swt_morning2/WordListTest.java b/app/src/test/java/com/swt20/swt_morning2/WordListTest.java new file mode 100644 index 0000000..545f90e --- /dev/null +++ b/app/src/test/java/com/swt20/swt_morning2/WordListTest.java @@ -0,0 +1,45 @@ +package com.swt20.swt_morning2; + + +import com.google.gson.Gson; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + + +@RunWith(MockitoJUnitRunner.class) +public class WordListTest { + + @Test + public void testWordListJsonParsing() { + boolean found = false; + Gson gson = new Gson(); + HangmanGameFragment.WordList wordList = gson.fromJson("{\n" + + " \"standardWords\": [\n" + + " \"Test\",\n" + + " \"Apfelbaum\",\n" + + " \"Auto\",\n" + + " \"Mensch\",\n" + + " \"Chrysantheme\",\n" + + " \"Kernspintomografie\",\n" + + " \"Zucchini\",\n" + + " \"Rueckgrat\",\n" + + " \"unentgeltlich\",\n" + + " \"Verlies\",\n" + + " \"Terrasse\",\n" + + " \"Quarzuhr\"\n" + + " ],\n" + + " \"customWords\": [\n" + + " ]\n" + + "}", HangmanGameFragment.WordList.class); + + for (int i = 0; i < 100; i++) { + if (wordList.getRandomWord().equalsIgnoreCase("Apfelbaum")) { + found = true; + break; + } + } + assert (found); + } +} From e2c0b546caecebe5ca6b672a9c67d417f12688d3 Mon Sep 17 00:00:00 2001 From: Gerald Birngruber Date: Wed, 29 Apr 2020 11:33:42 +0200 Subject: [PATCH 2/4] GAMES_006 Hangman Game logic [BG, SS] --- app/build.gradle | 3 +- .../swt_morning2/HangmanGameFragment.java | 160 ++++++++++++++++++ .../swt_morning2/HangmanMenuFragment.java | 35 ++++ .../swt20/swt_morning2/MainMenuFragment.java | 7 +- app/src/main/res/layout/hangman_game.xml | 60 +++++++ app/src/main/res/layout/hangman_menu.xml | 20 +++ app/src/main/res/navigation/nav_graph.xml | 29 ++++ app/src/main/res/values-de-rAT/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + 9 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/swt20/swt_morning2/HangmanGameFragment.java create mode 100644 app/src/main/java/com/swt20/swt_morning2/HangmanMenuFragment.java create mode 100644 app/src/main/res/layout/hangman_game.xml create mode 100644 app/src/main/res/layout/hangman_menu.xml diff --git a/app/build.gradle b/app/build.gradle index 8f81e77..0ebf30d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -91,5 +91,6 @@ dependencies { // https://mvnrepository.com/artifact/org.mockito/mockito-all testImplementation group: 'org.mockito', name: 'mockito-all', version: '1.10.19' testImplementation 'org.robolectric:robolectric:4.3' - androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test:rules:1.1.0' + implementation 'com.google.code.gson:gson:2.8.6' } diff --git a/app/src/main/java/com/swt20/swt_morning2/HangmanGameFragment.java b/app/src/main/java/com/swt20/swt_morning2/HangmanGameFragment.java new file mode 100644 index 0000000..5ed0208 --- /dev/null +++ b/app/src/main/java/com/swt20/swt_morning2/HangmanGameFragment.java @@ -0,0 +1,160 @@ +package com.swt20.swt_morning2; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.SystemClock; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.navigation.fragment.NavHostFragment; + +import com.google.gson.Gson; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Random; + +import static androidx.core.content.ContextCompat.getSystemService; + +public class HangmanGameFragment extends Fragment { + private static final String TAG = "[HangmanGameFragment]"; + private InputMethodManager imgr; + private TextView nextChar; + private TextView textView; + private String word2guess; + private String word2guess_viewtext = ""; + private TextView textViewWord2Guess; + private WordList wordList; + private SharedPreferences sharedPreferences; + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState + ) { + // Inflate the layout for this fragment + + String lang = Locale.getDefault().getDisplayLanguage(); + if(lang.contains("de")) + { + sharedPreferences = Objects.requireNonNull(getContext()).getSharedPreferences("HANGMAN_WORDS_DE", 0); + } else { + sharedPreferences = Objects.requireNonNull(getContext()).getSharedPreferences("HANGMAN_WORDS_EN", 0); + } + String wordsJson = sharedPreferences.getString("WORDS",getString(R.string.hangman_default_words)); + Gson gson = new Gson(); + wordList = gson.fromJson(wordsJson, WordList.class); + + return inflater.inflate(R.layout.hangman_game, container, false); + } + + public void onViewCreated(@NonNull final View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + nextChar = view.findViewById(R.id.plainText_nextChar); + textView = view.findViewById(R.id.textView); + imgr = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + textViewWord2Guess = view.findViewById(R.id.textView_word2guess); + //TODO TCs + + word2guess = wordList.getRandomWord().toUpperCase(); + for (int i = 0; i < word2guess.length(); i++) { + word2guess_viewtext += "_ "; + } + textViewWord2Guess.setText(word2guess_viewtext); + + view.findViewById(R.id.button_playagain).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + word2guess = wordList.getRandomWord().toUpperCase(); + word2guess_viewtext = ""; + for (int i = 0; i < word2guess.length(); i++) { + word2guess_viewtext += "_ "; + } + textViewWord2Guess.setText(word2guess_viewtext); + textView.setVisibility(View.VISIBLE); + nextChar.setVisibility(View.VISIBLE); + view.findViewById(R.id.button_playagain).setVisibility(View.INVISIBLE); + } + }); + + nextChar.addTextChangedListener(new TextWatcher() { + public void afterTextChanged(Editable s) { + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + String chrs = nextChar.getText().toString().toUpperCase(); + if (chrs.isEmpty()) { + Log.i(TAG, "chrs empty"); + return; + } + char chr = chrs.charAt(0); + Log.i(TAG, "" + (int) chr); + if (chr == ' ') { + return; + } + if (word2guess.contains(Character.toString(chr))) { + int idx = word2guess.indexOf(chr); + StringBuilder newText = new StringBuilder(word2guess_viewtext); + while (idx >= 0) { + newText.setCharAt(idx * 2, chr); + idx = word2guess.indexOf(chr, idx + 1); + } + word2guess_viewtext = newText.toString(); + textViewWord2Guess.setText(word2guess_viewtext); + } + nextChar.setText(""); + + String result = word2guess_viewtext.replace(" ", ""); + if (word2guess.equalsIgnoreCase(result)) { + ScoreTracker st = new ScoreTracker(getContext()); + st.addScore(Game.HANGMAN, 1); + Toast.makeText(getContext(), getString(R.string.hangman_WIN), Toast.LENGTH_LONG).show(); + + view.findViewById(R.id.button_playagain).setVisibility(View.VISIBLE); + textView.setVisibility(View.INVISIBLE); + nextChar.setVisibility(View.INVISIBLE); + + } + } + }); + + nextChar.requestFocus(); + imgr.showSoftInput(nextChar, InputMethodManager.SHOW_IMPLICIT); + } + + public class WordList { + public List standardWords; + public List customWords; + + public String getRandomWord() { + List tmpList = new ArrayList(); + tmpList.addAll(standardWords); + tmpList.addAll(customWords); + Random rand = new Random(); + return tmpList.get(rand.nextInt(tmpList.size())); + } + } +} diff --git a/app/src/main/java/com/swt20/swt_morning2/HangmanMenuFragment.java b/app/src/main/java/com/swt20/swt_morning2/HangmanMenuFragment.java new file mode 100644 index 0000000..da8d64d --- /dev/null +++ b/app/src/main/java/com/swt20/swt_morning2/HangmanMenuFragment.java @@ -0,0 +1,35 @@ +package com.swt20.swt_morning2; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.navigation.fragment.NavHostFragment; + +public class HangmanMenuFragment extends Fragment { + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState + ) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.hangman_menu, container, false); + } + + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + view.findViewById(R.id.ttt_menu_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + NavHostFragment.findNavController(HangmanMenuFragment.this) + .navigate(R.id.action_Menu_to_Game); + } + }); + + } +} diff --git a/app/src/main/java/com/swt20/swt_morning2/MainMenuFragment.java b/app/src/main/java/com/swt20/swt_morning2/MainMenuFragment.java index dbbd7c8..8902e49 100644 --- a/app/src/main/java/com/swt20/swt_morning2/MainMenuFragment.java +++ b/app/src/main/java/com/swt20/swt_morning2/MainMenuFragment.java @@ -1,7 +1,5 @@ package com.swt20.swt_morning2; -import android.content.res.Resources; -import android.icu.text.AlphabeticIndex; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; @@ -55,11 +53,12 @@ public void onClick(View view) { public void onClick(View view) { Log.i(TAG, "hangman button pressed"); // todo start appropriate game later - //NavHostFragment.findNavController(com.swt20.swt_morning2.MainMenuFragment.this) - // .navigate(R.id.action_FirstFragment_to_SecondFragment); + NavHostFragment.findNavController(com.swt20.swt_morning2.MainMenuFragment.this) + .navigate(R.id.action_mainMenuFragment_to_HangmanMenu); } }); + view.findViewById(R.id.whiteTilesButton).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { diff --git a/app/src/main/res/layout/hangman_game.xml b/app/src/main/res/layout/hangman_game.xml new file mode 100644 index 0000000..baa56f5 --- /dev/null +++ b/app/src/main/res/layout/hangman_game.xml @@ -0,0 +1,60 @@ + + + + + + + + + + +