diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e63c438
--- /dev/null
+++ b/README.md
@@ -0,0 +1,48 @@
+"Ma réu"
+
+
+Cette application permet aux collaborateurs de Lamzone de réserver une salle pour une réunion.
+
+Développé sur Android Studio, Java 8, APImax 21.
+Fonctions principales
+
+ Afficher les différentes réunions
+ Afficher en détail chacune des réunions
+ Filtrer ces réunions selon une date ou une salle
+ Ajouter une réunion
+ Supprimer une réunion
+
+Installation
+
+ Récupérer le programme via le bouton Download ou en utilisant git clone via ce lien https://github.com/SWvp/P4_Formation_OC/tree/v3
+
+ Lancer Android Studio, Open/File et choisir le fichier téléchargé
+
+Lancement
+
+Dans Android Studio :
+
+ Choisir un émulateur (virtuel ou réel)
+
+ Cliquer sur Run
+
+Bibliothèques utilisées
+
+ Material.io
+ RecyclerView
+ Cardview
+ CircleImageView
+ Lifecycle
+
+ Junit
+ Mockito
+
+ Architecture
+
+ MVVM
+
+ Renseignements supplémentaires sur les fonctions présentes
+
+ LiveData
+ Mediator
+ SingleLiveEvent
diff --git a/Test Results - AddMeetingActivityInstrumentedTest.html b/Test Results - AddMeetingActivityInstrumentedTest.html
new file mode 100644
index 0000000..8c5ed95
--- /dev/null
+++ b/Test Results - AddMeetingActivityInstrumentedTest.html
@@ -0,0 +1,646 @@
+
+
+
+
+Test Results — AddMeetingActivityTest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+20.03 s
+ com.kardabel.mareu.AddMeetingActivityInstrumentedTest
+
+
+
+3.45 s
+ passed selected_start_time_shows_hour
+
+
+
+2.35 s
+ passed selected_end_time_is_out_of_bound_should_not_display_time
+
+
+
+2.55 s
+ passed selected_end_time_shows_hour
+
+
+
+2.73 s
+ passed add_email_button_add_chip_with_email_inside
+
+
+
+2.37 s
+ passed selected_date_shows_date
+
+
+
+2.94 s
+ passed add_email_button_show_toast_when_wrong_email_entry
+
+
+
+1.44 s
+ passed save_button_stay_on_addMeeting_activity_when_reunion_is_not_complete
+
+
+
+2.21 s
+ passed selected_start_time_is_out_of_bound_should_not_display_time
+
+
+
+
+
+
+
+
+
diff --git a/Test Results - DetailsActivityInstrumentedTest.html b/Test Results - DetailsActivityInstrumentedTest.html
new file mode 100644
index 0000000..bb69395
--- /dev/null
+++ b/Test Results - DetailsActivityInstrumentedTest.html
@@ -0,0 +1,631 @@
+
+
+
+
+Test Results — DetailsActivityTest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+9.32 s
+ com.kardabel.mareu.DetailsActivityInstrumentedTest
+
+
+
+2.55 s
+ passed details_Activity_display_time_with_0_first_number_if_inferior_to_ten
+
+
+
+1.83 s
+ passed details_activity_display_neighbour_name
+
+
+
+1.62 s
+ passed details_Activity_display_date
+
+
+
+1.68 s
+ passed details_Activity_display_time
+
+
+
+1.64 s
+ passed details_Activity_display_room_name
+
+
+
+
+
+
+
+
+
diff --git a/Test Results - MainActivityInstrumentedTest.html b/Test Results - MainActivityInstrumentedTest.html
new file mode 100644
index 0000000..93d82a1
--- /dev/null
+++ b/Test Results - MainActivityInstrumentedTest.html
@@ -0,0 +1,646 @@
+
+
+
+
+Test Results — MainActivityInstrume...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+22.84 s
+ com.kardabel.mareu.MainActivityInstrumentedTest
+
+
+
+2.37 s
+ passed click_on_recyclerview_item_should_display_details_activity
+
+
+
+1.42 s
+ passed recyclerview_delete_action_should_remove_one_meeting
+
+
+
+2.37 s
+ passed choose_a_room_menu_should_display_filtered_list
+
+
+
+1.90 s
+ passed click_on_FAB_should_display_add_meeting_activity
+
+
+
+7.60 s
+ passed save_button_shows_main_activity_with_new_meeting_if_meeting_fields_complete
+
+
+
+3.19 s
+ passed reset_filters_should_display_all_meetings
+
+
+
+1.26 s
+ passed recyclerview_should_now_display_four_meetings
+
+
+
+2.73 s
+ passed choose_a_date_menu_should_display_filtered_list
+
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 49f38fb..b7e3f08 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,6 +22,10 @@ android {
}
}
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
+
buildTypes {
release {
@@ -30,12 +34,17 @@ android {
}
}
compileOptions {
+ coreLibraryDesugaringEnabled true
+
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+
+ implementation 'de.hdodenhof:circleimageview:3.1.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
@@ -43,16 +52,19 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.2.0-rc01'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
- implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.github.bumptech.glide:glide:4.12.0'
- implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
-
-
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
- testImplementation 'junit:junit:4.+'
+ testImplementation 'android.arch.core:core-testing:1.1.1'
+ testImplementation 'junit:junit:4.13.2'
+ //Mockito
+ testImplementation 'org.mockito:mockito-core:3.8.0'
+ testImplementation 'org.mockito:mockito-inline:3.8.0'
+ //Instrumented
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.2'
}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/kardabel/mareu/AddMeetingActivityInstrumentedTest.java b/app/src/androidTest/java/com/kardabel/mareu/AddMeetingActivityInstrumentedTest.java
new file mode 100644
index 0000000..067ae59
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/AddMeetingActivityInstrumentedTest.java
@@ -0,0 +1,141 @@
+package com.kardabel.mareu;
+
+import android.widget.DatePicker;
+import android.widget.TimePicker;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.ViewAssertion;
+import androidx.test.espresso.action.ViewActions;
+import androidx.test.espresso.contrib.PickerActions;
+import androidx.test.espresso.matcher.ViewMatchers;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import com.google.android.material.chip.ChipGroup;
+import com.kardabel.mareu.ui.add.AddMeetingActivity;
+import com.kardabel.mareu.utils.ToastMatcher;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static android.service.autofill.Validators.not;
+import static androidx.test.espresso.Espresso.onData;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.kardabel.mareu.utils.RecyclerViewItemCountAssertion.withItemCount;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.core.AllOf.allOf;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+@RunWith(JUnit4.class)
+public class AddMeetingActivityInstrumentedTest {
+ @Rule
+ public ActivityScenarioRule mActivityScenarioRule =
+ new ActivityScenarioRule(AddMeetingActivity.class);
+
+ @Before
+ public void launchActivity() { ActivityScenario.launch(AddMeetingActivity.class); }
+
+ @Test
+ public void selected_date_shows_date(){
+ // When: click on the date text and choose a date via datepicker
+ onView(ViewMatchers.withId(R.id.date_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(DatePicker.class)).perform(PickerActions.setDate(2021, 06, 12));
+ onView(withText("OK")).perform(click());
+ // Then: text shows date picked
+ onView(withId(R.id.date_setter)).check(matches(allOf(withText("2021-06-12"), isDisplayed())));
+
+ }
+
+ @Test
+ public void selected_start_time_shows_hour(){
+ // When: click on the start time text and choose a time via timepicker
+ onView(ViewMatchers.withId(R.id.start_time_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(TimePicker.class)).perform(PickerActions.setTime(12, 05));
+ onView(withText("OK")).perform(click());
+ // Then: text shows time picked
+ onView(withId(R.id.start_time_setter)).check(matches(allOf(withText("12:05"), isDisplayed())));
+
+ }
+
+ @Test
+ public void selected_end_time_shows_hour(){
+ // When: click on the end time text and choose a time via timePicker
+ onView(ViewMatchers.withId(R.id.end_time_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(TimePicker.class)).perform(PickerActions.setTime(14, 21));
+ onView(withText("OK")).perform(click());
+ // Then: text shows time picked
+ onView(withId(R.id.end_time_setter)).check(matches(allOf(withText("14:21"), isDisplayed())));
+
+ }
+
+ @Test
+ public void selected_start_time_is_out_of_bound_should_not_display_time(){
+ // When: click on the start time text and choose a time via timepicker
+ onView(ViewMatchers.withId(R.id.start_time_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(TimePicker.class)).perform(PickerActions.setTime(05, 00));
+ onView(withText("OK")).perform(click());
+ // Then: text don't shows time picked
+ onView(withId(R.id.start_time_setter)).check(matches(allOf(withText(""), isDisplayed())));
+
+ }
+
+ @Test
+ public void selected_end_time_is_out_of_bound_should_not_display_time(){
+ // When: click on the end time text and choose a time via timepicker
+ onView(ViewMatchers.withId(R.id.end_time_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(TimePicker.class)).perform(PickerActions.setTime(19, 33));
+ onView(withText("OK")).perform(click());
+ // Then: text don't shows time picked
+ onView(withId(R.id.end_time_setter)).check(matches(allOf(withText(""), isDisplayed())));
+
+ }
+
+ @Test
+ public void add_email_button_add_chip_with_email_inside(){
+ // When: email is added
+ onView(ViewMatchers.withId(R.id.email_input)).perform(scrollTo(), click());
+ onView(ViewMatchers.withId(R.id.email_text)).perform(replaceText("test@test.com"));
+ onView(ViewMatchers.withId(R.id.add_mail_button)).perform(scrollTo(), click());
+ onView(ViewMatchers.isRoot()).perform(ViewActions.closeSoftKeyboard());
+ // Then: chipGroup is updated with new email
+ onView(withId(R.id.mail_chip_model)).check(matches(allOf(withText("test@test.com"), isDisplayed())));
+
+ }
+
+ @Test
+ public void add_email_button_show_toast_when_wrong_email_entry(){
+ // When: email is added
+ onView(ViewMatchers.withId(R.id.email_input)).perform(scrollTo(), click());
+ onView(ViewMatchers.withId(R.id.email_text)).perform(replaceText("testtest.com@"));
+ onView(ViewMatchers.withId(R.id.create)).perform(scrollTo(), click());
+ onView(ViewMatchers.isRoot()).perform(ViewActions.closeSoftKeyboard());
+ // Then: chipGroup do not shows email and toast popup
+
+ }
+
+ @Test
+ public void save_button_stay_on_addMeeting_activity_when_reunion_is_not_complete(){
+ // When: click on save button before complete fields
+ onView(ViewMatchers.withId(R.id.create)).perform(scrollTo(), click());
+ // Then:
+ assertFalse(mActivityScenarioRule.getScenario() == null);
+
+ }
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/DetailsActivityInstrumentedTest.java b/app/src/androidTest/java/com/kardabel/mareu/DetailsActivityInstrumentedTest.java
new file mode 100644
index 0000000..ac74b81
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/DetailsActivityInstrumentedTest.java
@@ -0,0 +1,81 @@
+package com.kardabel.mareu;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.contrib.RecyclerViewActions;
+import androidx.test.espresso.matcher.ViewMatchers;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.kardabel.mareu.ui.list.MainActivity;
+import com.kardabel.mareu.utils.ItemViewAction;
+
+import org.junit.Before;
+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.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+
+@RunWith(AndroidJUnit4.class)
+public class DetailsActivityInstrumentedTest {
+ @Rule
+ public ActivityScenarioRule mActivityScenarioRule =
+ new ActivityScenarioRule(MainActivity.class);
+
+ @Before
+ public void launchActivity() { ActivityScenario.launch(MainActivity.class); }
+
+ @Test
+ public void details_activity_display_neighbour_name(){
+ // When: click on recyclerview item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(2, new ItemViewAction()));
+ // Then: display meeting name
+ onView(withId(R.id.detail_meeting_name)).check(matches((withText("Reunion c"))));
+
+ }
+
+ @Test
+ public void details_Activity_display_room_name(){
+ // When: click on recyclerview item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(0, new ItemViewAction()));
+ // Then: display meeting room name
+ onView(withId(R.id.detail_room_name)).check(matches((withText("Mario"))));
+
+ }
+
+ @Test
+ public void details_Activity_display_date(){
+ // When: click on recyclerview item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(1, new ItemViewAction()));
+ // Then: display meeting date
+ onView(withId(R.id.detail_date)).check(matches((withText("2021-05-01"))));
+
+ }
+
+ @Test
+ public void details_Activity_display_time(){
+ // When: click on recyclerview item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(3, new ItemViewAction()));
+ // Then: display meeting time
+ onView(withId(R.id.detail_hour)).check(matches((withText("16:50 to 18:00"))));
+
+ }
+
+ @Test
+ public void details_Activity_display_time_with_0_first_number_if_inferior_to_ten(){
+ // When: click on recyclerview item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(1, new ItemViewAction()));
+ // Then: display meeting time
+ onView(withId(R.id.detail_hour)).check(matches((withText("09:00 to 11:20"))));
+
+ }
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/kardabel/mareu/ExampleInstrumentedTest.java
deleted file mode 100644
index 157412e..0000000
--- a/app/src/androidTest/java/com/kardabel/mareu/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.kardabel.mareu;
-
-import android.content.Context;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
- assertEquals("com.kardabel.mareu", appContext.getPackageName());
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/kardabel/mareu/MainActivityInstrumentedTest.java b/app/src/androidTest/java/com/kardabel/mareu/MainActivityInstrumentedTest.java
new file mode 100644
index 0000000..3ef759e
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/MainActivityInstrumentedTest.java
@@ -0,0 +1,161 @@
+package com.kardabel.mareu;
+
+import android.widget.DatePicker;
+import android.widget.TimePicker;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.action.ViewActions;
+import androidx.test.espresso.contrib.PickerActions;
+import androidx.test.espresso.contrib.RecyclerViewActions;
+import androidx.test.espresso.matcher.ViewMatchers;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.kardabel.mareu.ui.list.MainActivity;
+import com.kardabel.mareu.utils.DeleteViewAction;
+import com.kardabel.mareu.utils.ItemViewAction;
+
+import org.junit.Before;
+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.replaceText;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
+import static androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static com.kardabel.mareu.utils.RecyclerViewItemCountAssertion.withItemCount;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertTrue;
+
+
+@RunWith(AndroidJUnit4.class)
+public class MainActivityInstrumentedTest {
+
+ private int ITEMS_COUNT = 4;
+
+ @Rule
+ public ActivityScenarioRule rule = new ActivityScenarioRule<>(MainActivity.class);
+
+ @Before
+ public void launchActivity() { ActivityScenario.launch(MainActivity.class); }
+
+ @Test
+ public void click_on_recyclerview_item_should_display_details_activity(){
+ // Given: check if 4 meetings on board
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+ // When: click on recyclerview item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(2, new ItemViewAction()));
+ // Then: got to details activity
+ onView(ViewMatchers.withId(R.id.detail_room_avatar)).check(matches(isDisplayed()));
+
+ }
+
+ @Test
+ public void recyclerview_delete_action_should_remove_one_meeting() {
+ // Given: check if 4 meetings on board
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+ // When: click on delete button on item
+ onView(ViewMatchers.withId(R.id.recyclerView))
+ .perform(RecyclerViewActions.actionOnItemAtPosition(1, new DeleteViewAction()));
+ // Then: one meeting less on board
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT - 1));
+
+ }
+
+ @Test
+ public void choose_a_room_menu_should_display_filtered_list(){
+ // Given: check if now 3 meetings on board
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT - 1));
+ // When: click on specific room
+ onView(withId(R.id.filters)).perform(click());
+ onView(withText("Choose a room")).perform(click());
+ onView(withText("Mario")).perform(click());
+ // Then: got filtered list with room choice only
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(1));
+
+ }
+
+ @Test
+ public void click_on_FAB_should_display_add_meeting_activity(){
+ // When: click on the fab button
+ onView(ViewMatchers.withId(R.id.add_meeting_button)).perform(click());
+ // Then: Go to addMeeting activity
+ onView(ViewMatchers.withId(R.id.add_meeting)).check(matches(isDisplayed()));
+
+ }
+
+ @Test
+ public void save_button_should_shows_main_activity_with_new_meeting_if_meeting_fields_complete(){
+ // Check number of item (we delete one before)
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT - 1));
+ // Navigate to add meeting activity
+ onView(ViewMatchers.withId(R.id.add_meeting_button)).perform(click());
+ // When: click on the fab button and check all fields with correct data
+ onView(ViewMatchers.withId(R.id.meeting_name_input)).perform(replaceText("test 1"));
+ onView(ViewMatchers.withId(R.id.dropdown_autocomplete)).perform(replaceText("Luigi"));
+ onView(ViewMatchers.withId(R.id.date_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(DatePicker.class)).perform(PickerActions.setDate(2022, 10, 3));
+ onView(withText("OK")).perform(click());
+ onView(ViewMatchers.withId(R.id.start_time_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(TimePicker.class)).perform(PickerActions.setTime(8, 05));
+ onView(withText("OK")).perform(click());
+ onView(ViewMatchers.withId(R.id.end_time_setter)).perform(scrollTo(), click());
+ onView(isAssignableFrom(TimePicker.class)).perform(PickerActions.setTime(10, 55));
+ onView(withText("OK")).perform(click());
+ onView(ViewMatchers.withId(R.id.email_input)).perform(scrollTo(), click());
+ onView(ViewMatchers.withId(R.id.email_text)).perform(replaceText("test@test.com"));
+ onView(ViewMatchers.isRoot()).perform(ViewActions.closeSoftKeyboard());
+ onView(ViewMatchers.withId(R.id.add_mail_button)).perform(scrollTo(), click());
+ onView(ViewMatchers.withId(R.id.create)).perform(click());
+ // Then: 5 meetings on main view
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+
+ }
+
+ @Test
+ public void reset_filters_should_display_all_meetings(){
+ // Given: check if 4 meetings on board (one was delete, and then one was added)
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+ // When: click on specific room
+ onView(withId(R.id.filters)).perform(click());
+ onView(withText("Choose a room")).perform(click());
+ onView(withText("Boo")).perform(click());
+ // Then: we got filtered list
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT - 3));
+ // and finally reset filters
+ onView(withId(R.id.filters)).perform(click());
+ onView(withText("RESET FILTERS")).perform(click());
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+
+ }
+
+ @Test
+ public void choose_a_date_menu_should_display_filtered_list(){
+ // Given: check if 4 meetings on board
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+ // When: click on specific date
+ onView(withId(R.id.filters)).perform(click());
+ onView(withText("Choose a date")).perform(click());
+ onView(isAssignableFrom(DatePicker.class)).perform(PickerActions.setDate(2021, 06, 15));
+ onView(withText("OK")).perform(click());
+ // Then: got filtered list
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(1));
+
+ }
+
+ @Test
+ public void recyclerview_should_now_display_four_meetings() {
+ onView(ViewMatchers.withId(R.id.recyclerView)).check(withItemCount(ITEMS_COUNT));
+
+ }
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/utils/DeleteViewAction.java b/app/src/androidTest/java/com/kardabel/mareu/utils/DeleteViewAction.java
new file mode 100644
index 0000000..3897574
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/utils/DeleteViewAction.java
@@ -0,0 +1,34 @@
+package com.kardabel.mareu.utils;
+
+import android.view.View;
+
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+
+import com.kardabel.mareu.R;
+
+import org.hamcrest.Matcher;
+
+/**
+ * Created by stéphane Warin OCR on 29/04/2021.
+ */
+public class DeleteViewAction implements ViewAction {
+
+ @Override
+ public Matcher getConstraints() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Click on specific button";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ View button = view.findViewById(R.id.delete_button);
+ button.performClick();
+ }
+
+
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/utils/DrawableMatcher.java b/app/src/androidTest/java/com/kardabel/mareu/utils/DrawableMatcher.java
new file mode 100644
index 0000000..63d6eff
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/utils/DrawableMatcher.java
@@ -0,0 +1,26 @@
+package com.kardabel.mareu.utils;
+
+import android.view.View;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+
+public class DrawableMatcher extends TypeSafeMatcher {
+ private final int expectedId;
+
+ public DrawableMatcher(int resourceId) {
+ super(View.class);
+ this.expectedId = resourceId;
+ }
+
+ @Override
+ protected boolean matchesSafely(View item) {
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+
+ }
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/utils/ItemViewAction.java b/app/src/androidTest/java/com/kardabel/mareu/utils/ItemViewAction.java
new file mode 100644
index 0000000..bef162e
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/utils/ItemViewAction.java
@@ -0,0 +1,30 @@
+package com.kardabel.mareu.utils;
+
+import android.view.View;
+
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+
+import com.kardabel.mareu.R;
+
+import org.hamcrest.Matcher;
+
+
+public class ItemViewAction implements ViewAction {
+ @Override
+ public Matcher getConstraints() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Click on specific button";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ View button = view.findViewById(R.id.item);
+ // Maybe check for null
+ button.performClick();
+ }
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/utils/RecyclerViewItemCountAssertion.java b/app/src/androidTest/java/com/kardabel/mareu/utils/RecyclerViewItemCountAssertion.java
new file mode 100644
index 0000000..e90722b
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/utils/RecyclerViewItemCountAssertion.java
@@ -0,0 +1,41 @@
+package com.kardabel.mareu.utils;
+
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewAssertion;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+
+/**
+ * Created by stéphane Warin OCR on 29/04/2021.
+ */
+public class RecyclerViewItemCountAssertion implements ViewAssertion {
+ private final Matcher matcher;
+
+ public static RecyclerViewItemCountAssertion withItemCount(int expectedCount) {
+ return withItemCount(Matchers.is(expectedCount));
+ }
+
+ public static RecyclerViewItemCountAssertion withItemCount(Matcher matcher) {
+ return new RecyclerViewItemCountAssertion(matcher);
+ }
+
+ private RecyclerViewItemCountAssertion(Matcher matcher) {
+ this.matcher = matcher;
+ }
+
+ @Override
+ public void check(View view, NoMatchingViewException noViewFoundException) {
+ if (noViewFoundException != null) {
+ throw noViewFoundException;
+ }
+
+ RecyclerView recyclerView = (RecyclerView) view;
+ RecyclerView.Adapter adapter = recyclerView.getAdapter();
+ Assert.assertThat(adapter.getItemCount(), matcher);
+ }
+}
diff --git a/app/src/androidTest/java/com/kardabel/mareu/utils/ToastMatcher.java b/app/src/androidTest/java/com/kardabel/mareu/utils/ToastMatcher.java
new file mode 100644
index 0000000..a27040f
--- /dev/null
+++ b/app/src/androidTest/java/com/kardabel/mareu/utils/ToastMatcher.java
@@ -0,0 +1,34 @@
+package com.kardabel.mareu.utils;
+
+import android.os.IBinder;
+import android.view.WindowManager;
+
+import androidx.test.espresso.Root;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+public class ToastMatcher extends TypeSafeMatcher {
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is toast");
+
+ }
+
+ @Override
+ public boolean matchesSafely(Root root) {
+ int type = root.getWindowLayoutParams().get().type;
+ if ((type == WindowManager.LayoutParams.TYPE_TOAST)) {
+ IBinder windowToken = root.getDecorView().getWindowToken();
+ IBinder appToken = root.getDecorView().getApplicationWindowToken();
+ if (windowToken == appToken) {
+ return true;
+
+ }
+ }
+ return false;
+
+ }
+}
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e0ea6fe..b2db2cf 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,18 +10,22 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/Theme.Maréu">
+ android:theme="@style/Theme.Maréu"
+ >
+ android:name=".ui.list.DetailsActivity"
+ android:screenOrientation="sensor"/>
+ android:name=".ui.add.AddMeetingActivity"
+ android:screenOrientation="sensor" />
+ android:name=".ui.list.MainActivity"
+ android:screenOrientation="sensor">
diff --git a/app/src/main/java/com/kardabel/mareu/data/FakeDataStore.java b/app/src/main/java/com/kardabel/mareu/data/FakeDataStore.java
new file mode 100644
index 0000000..c8ddfe4
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/data/FakeDataStore.java
@@ -0,0 +1,40 @@
+package com.kardabel.mareu.data;
+
+import com.kardabel.mareu.model.Email;
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.model.Room;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+public class FakeDataStore {
+
+ public static List Fake_Meetings = Arrays.asList(
+ new Meeting(0, "Reunion A", Room.ROOM_MARIO, LocalTime.of(15, 10), LocalTime.of(16, 50), LocalDate.of(2021, 05, 12), Room.ROOM_MARIO, Arrays.asList(
+ new Email("stephane@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(1, "Reunion B", Room.ROOM_PEACH, LocalTime.of(9, 00), LocalTime.of(11, 20), LocalDate.of(2021, 05, 01), Room.ROOM_PEACH, Arrays.asList(
+ new Email("warin@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(2, "Reunion c", Room.ROOM_GOOMBA, LocalTime.of(11, 8), LocalTime.of(13,4), LocalDate.of(2021, 06, 15), Room.ROOM_GOOMBA, Arrays.asList(
+ new Email("philibert@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(3, "Reunion D", Room.ROOM_BOO, LocalTime.of(16, 50), LocalTime.of(18,00), LocalDate.of(2021, 10, 02), Room.ROOM_BOO, Arrays.asList(
+ new Email("krabulbe@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ ))
+
+ );
+
+ public static List generateMeetingList() {
+ return new ArrayList<>(Fake_Meetings);
+ }
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/di/MareuViewModelFactory.java b/app/src/main/java/com/kardabel/mareu/di/MareuViewModelFactory.java
new file mode 100644
index 0000000..93fc6cf
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/di/MareuViewModelFactory.java
@@ -0,0 +1,53 @@
+package com.kardabel.mareu.di;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.kardabel.mareu.repository.MeetingsRepository;
+import com.kardabel.mareu.ui.add.AddMeetingViewModel;
+import com.kardabel.mareu.ui.details.DetailsViewModel;
+import com.kardabel.mareu.ui.list.MainViewModel;
+
+
+public class MareuViewModelFactory implements ViewModelProvider.Factory {
+
+ private static MareuViewModelFactory sFactory;
+ private final MeetingsRepository mMeetingsRepository;
+
+ public static MareuViewModelFactory getInstance() {
+ if (sFactory == null) {
+ synchronized (MareuViewModelFactory.class) {
+ if (sFactory == null) {
+ sFactory = new MareuViewModelFactory();
+
+ }
+ }
+ }
+ return sFactory;
+
+ }
+
+ public MareuViewModelFactory() {
+ mMeetingsRepository = new MeetingsRepository();
+ }
+
+ // Create an instance for each viewModel
+ @NonNull
+ @Override
+ public T create(Class modelClass) {
+ if (modelClass.isAssignableFrom(MainViewModel.class)) {
+ return (T) new MainViewModel(mMeetingsRepository);
+
+ } else if (modelClass.isAssignableFrom(DetailsViewModel.class)) {
+ return (T) new DetailsViewModel(mMeetingsRepository);
+
+ } else if (modelClass.isAssignableFrom(AddMeetingViewModel.class)) {
+ return (T) new AddMeetingViewModel(mMeetingsRepository);
+
+ }
+
+ throw new IllegalArgumentException("Unknown ViewModel class");
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/data/FakeDataStore.java b/app/src/main/java/com/kardabel/mareu/mareu/data/FakeDataStore.java
deleted file mode 100644
index 3aef23a..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/data/FakeDataStore.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.kardabel.mareu.mareu.data;
-
-import com.kardabel.mareu.mareu.model.Meeting;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Created by stéphane Warin OCR on 31/03/2021.
- */
-public class FakeDataStore {
-
- public static List Fake_Meetings = Arrays.asList(
- new Meeting(1,"Reuninon A","https://www.nicepng.com/png/full/32-328642_user-avatar-super-mario-avatar.png", "11h20", "Mario", "blake@gpamail.com"),
- new Meeting(2,"Reuninon B","https://www.nicepng.com/png/full/6-62950_paper-peach-color-splash-paper-mario-color-splash.png", "08h20", "Peach", "garogorille@gtromail.com")
- );
-
-
- public static List generateMeetingList() {
-
- return new ArrayList<>(Fake_Meetings);
- }
-
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/di/MareuViewModelFactory.java b/app/src/main/java/com/kardabel/mareu/mareu/di/MareuViewModelFactory.java
deleted file mode 100644
index e7721fb..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/di/MareuViewModelFactory.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.kardabel.mareu.mareu.di;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.kardabel.mareu.mareu.repository.MeetingsRepository;
-import com.kardabel.mareu.mareu.viewmodel.MeetingActivityViewModel;
-
-/**
- * Created by stéphane Warin OCR on 01/04/2021.
- */
-public class MareuViewModelFactory implements ViewModelProvider.Factory {
-
- private static MareuViewModelFactory sFactory;
-
-
- private MareuViewModelFactory() {
- }
-
- public static MareuViewModelFactory getInstance() {
- if (sFactory == null) {
- synchronized (MareuViewModelFactory.class) {
- if (sFactory == null) {
- sFactory = new MareuViewModelFactory();
-
- }
- }
- }
-
- return sFactory;
- }
-
-
- @NonNull
- @Override
- public T create(Class modelClass) {
- if (modelClass.isAssignableFrom(MeetingActivityViewModel.class)) {
- return (T) new MeetingActivityViewModel(new MeetingsRepository());
- }
-
- throw new IllegalArgumentException("Unknown ViewModel class");
- }
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/model/Meeting.java b/app/src/main/java/com/kardabel/mareu/mareu/model/Meeting.java
deleted file mode 100644
index 51eead1..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/model/Meeting.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.kardabel.mareu.mareu.model;
-
-
-/**
- * Created by stéphane Warin OCR on 25/03/2021.
- */
-public class Meeting {
- private int meetingId;
- private String meetingDetails;
- private String hour;
- private String room;
- private String roomAvatar;
-
- private String mailingList;
-
- public Meeting(int meetingId, String meetingDetails, String avatar, String hour, String room, String mailingList) {
- this.meetingId = meetingId;
- this.meetingDetails = meetingDetails;
- this.hour = hour;
- this.room = room;
- this.roomAvatar = avatar;
- this.mailingList = mailingList;
- }
-
- public int getMeetingId(){return meetingId;}
-
- public String getMeetingDetails() { return meetingDetails; }
-
- public String getMeetingHour() { return hour; }
-
- public String getMeetingRoom() { return room; }
-
- public String getRoomAvatar() {
- return roomAvatar;
- }
-
- public String getMailingList() {
- return mailingList;
- }
-}
-
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/repository/MeetingsRepository.java b/app/src/main/java/com/kardabel/mareu/mareu/repository/MeetingsRepository.java
deleted file mode 100644
index a00d124..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/repository/MeetingsRepository.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.kardabel.mareu.mareu.repository;
-
-import android.app.Application;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-
-import com.kardabel.mareu.mareu.data.FakeDataStore;
-import com.kardabel.mareu.mareu.model.Meeting;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Created by stéphane Warin OCR on 31/03/2021.
- */
-public class MeetingsRepository {
- private MutableLiveData> mutableMeetingList = new MutableLiveData<>();
- private List nMeetings = FakeDataStore.generateMeetingList();
-
- //Modify meetings list
- public void insertMeeting(Meeting meeting){
- nMeetings.add(meeting);
-
- }
-
- public void deleteMeeting(Meeting meeting){
- nMeetings.remove(meeting);
-
- }
-
- public void deleteAllMeetings(){
-
- }
-
- //call the list of meetings from DataStore
- public LiveData> getMeetingsList() {
- mutableMeetingList.setValue(nMeetings);
- return mutableMeetingList;
- }
-
-
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/AddActivityViewModel.java b/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/AddActivityViewModel.java
deleted file mode 100644
index 01cb518..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/AddActivityViewModel.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.kardabel.mareu.mareu.viewmodel;
-
-/**
- * Created by stéphane Warin OCR on 08/04/2021.
- */
-public class AddActivityViewModel {
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/DetailsActivityViewModel.java b/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/DetailsActivityViewModel.java
deleted file mode 100644
index 1637619..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/DetailsActivityViewModel.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.kardabel.mareu.mareu.viewmodel;
-
-/**
- * Created by stéphane Warin OCR on 08/04/2021.
- */
-public class DetailsActivityViewModel {
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/MeetingActivityViewModel.java b/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/MeetingActivityViewModel.java
deleted file mode 100644
index 0ca4416..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/viewmodel/MeetingActivityViewModel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.kardabel.mareu.mareu.viewmodel;
-
-import android.app.Application;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.AndroidViewModel;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-import com.kardabel.mareu.mareu.model.Meeting;
-import com.kardabel.mareu.mareu.repository.MeetingsRepository;
-import com.kardabel.mareu.mareu.views.MeetingsViewState;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Created by stéphane Warin OCR on 26/03/2021.
- */
-public class MeetingActivityViewModel extends ViewModel {
- private MeetingsRepository mMeetingsRepository;
- private LiveData> meetingsList;
-
- public MeetingActivityViewModel(@NonNull MeetingsRepository meetingsRepository) {
- mMeetingsRepository = new MeetingsRepository();
- meetingsList = mMeetingsRepository.getMeetingsList();
-
- }
-
- public void insertMeeting(Meeting meeting){
- mMeetingsRepository.insertMeeting(meeting);
-
- }
-
- public void deleteMeeting(Meeting meeting){
- mMeetingsRepository.deleteMeeting(meeting);
- }
-
- public void deleteAllMeetings(){
- mMeetingsRepository.deleteAllMeetings();
-
- }
-
- public LiveData> getMeetingsList(){
- return meetingsList;
-
- }
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/views/AddMeetingActivity.java b/app/src/main/java/com/kardabel/mareu/mareu/views/AddMeetingActivity.java
deleted file mode 100644
index 9b83e38..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/views/AddMeetingActivity.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.kardabel.mareu.mareu.views;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import com.google.android.material.textfield.TextInputLayout;
-import com.kardabel.mareu.R;
-import com.kardabel.mareu.mareu.model.Meeting;
-
-/**
- * Created by stéphane Warin OCR on 26/03/2021.
- */
-public class AddMeetingActivity extends AppCompatActivity {
-
- private TextInputLayout mTextInputLayout;
- private AutoCompleteTextView mAutoCompleteTextView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_addmeeting);
-
- mTextInputLayout = findViewById(R.id.dropdown_menu);
- mAutoCompleteTextView = findViewById(R.id.dropdown_autocomplete);
-
- String[] items = new String[]{
- "Peach",
- "Mario",
- "Toad",
- "Bowser",
-
- };
-
- ArrayAdapter adapter = new ArrayAdapter<>(
- AddMeetingActivity.this,
- R.layout.activity_addmeeting_dropdown_item,
- items
-
- );
- mAutoCompleteTextView.setAdapter(adapter);
- }
-
-
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingDetailsActivity.java b/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingDetailsActivity.java
deleted file mode 100644
index 657bef2..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingDetailsActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.kardabel.mareu.mareu.views;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import com.kardabel.mareu.R;
-import com.kardabel.mareu.mareu.model.Meeting;
-
-/**
- * Created by stéphane Warin OCR on 26/03/2021.
- */
-public class MeetingDetailsActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_meetings_details);
-
- }
-
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsActivity.java b/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsActivity.java
deleted file mode 100644
index 5eee059..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsActivity.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.kardabel.mareu.mareu.views;
-
-
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-
-
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import com.kardabel.mareu.R;
-import com.kardabel.mareu.mareu.di.MareuViewModelFactory;
-import com.kardabel.mareu.mareu.model.Meeting;
-import com.kardabel.mareu.mareu.viewmodel.MeetingActivityViewModel;
-
-
-import java.util.List;
-
-public class MeetingsActivity extends AppCompatActivity {
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_mareu);
-
- RecyclerView mRecyclerView = findViewById(R.id.recyclerView);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
-
- MeetingsRecyclerViewAdapter mAdapter = new MeetingsRecyclerViewAdapter();
- mRecyclerView.setAdapter(mAdapter);
-
- final MeetingActivityViewModel mareuViewModel = new ViewModelProvider(this, MareuViewModelFactory.getInstance()).get(MeetingActivityViewModel.class);
-
-
- mareuViewModel.getMeetingsList().observe(this, new Observer>() {
- @Override
- public void onChanged(@Nullable List meetings ) {
- mAdapter.setMeetings(meetings);
- //mAdapter.submitList(meetings);
- }
- });
-
- FloatingActionButton floatingActionButton = findViewById(R.id.add_meeting_button);
- floatingActionButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(MeetingsActivity.this, AddMeetingActivity.class);
- startActivity(intent);
- }
- });
-
- mAdapter.setOnItemClickListener(new MeetingsRecyclerViewAdapter.OnItemClickListener() {
- @Override
- public void onMeetingItemClick(Meeting meeting) {
- Intent intent = new Intent(MeetingsActivity.this, MeetingDetailsActivity.class);
- startActivity(intent);
-
- }
- });
-
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsViewState.java b/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsViewState.java
deleted file mode 100644
index 3b4ea8c..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsViewState.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.kardabel.mareu.mareu.views;
-
-/**
- * Created by stéphane Warin OCR on 01/04/2021.
- */
-public class MeetingsViewState {
- private final String meetingDetails;
- private final String mailingList;
- private final String avatarUrl;
-
-
- public MeetingsViewState(String meetingDetails, String mailingList, String avatarUrl) {
- this.meetingDetails = meetingDetails;
- this.mailingList = mailingList;
- this.avatarUrl = avatarUrl;
- }
-
- public String getMeetingDetailsToDisplay(){
- return meetingDetails;
-
- }
-
- public String getMailingListToDisplay(){
- return mailingList;
-
- }
-
- public String getAvatarUrlToDisplay(){
- return avatarUrl;
-
- }
-}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/views/TimeDatePickerFragment.java b/app/src/main/java/com/kardabel/mareu/mareu/views/TimeDatePickerFragment.java
deleted file mode 100644
index ee00220..0000000
--- a/app/src/main/java/com/kardabel/mareu/mareu/views/TimeDatePickerFragment.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.kardabel.mareu.mareu.views;
-
-import androidx.fragment.app.DialogFragment;
-
-/**
- * Created by stéphane Warin OCR on 09/04/2021.
- */
-public class TimeDatePickerFragment extends DialogFragment {
-
-}
diff --git a/app/src/main/java/com/kardabel/mareu/model/Email.java b/app/src/main/java/com/kardabel/mareu/model/Email.java
new file mode 100644
index 0000000..3f125eb
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/model/Email.java
@@ -0,0 +1,13 @@
+package com.kardabel.mareu.model;
+
+public class Email {
+
+ private String url;
+
+ public Email(String url) { this.url = url; }
+
+ public String getUrl() {
+ return url;
+ }
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/model/Meeting.java b/app/src/main/java/com/kardabel/mareu/model/Meeting.java
new file mode 100644
index 0000000..733d596
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/model/Meeting.java
@@ -0,0 +1,106 @@
+package com.kardabel.mareu.model;
+
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+public class Meeting {
+ private int meetingId;
+ private String meetingName;
+ private LocalTime startTime;
+ private LocalTime endTime;
+ private LocalDate date;
+ private Room roomName;
+ private Room roomAvatar;
+ private List mailingList;
+
+ public Meeting(int meetingId, String meetingName, Room avatar, LocalTime startTime, LocalTime endTime, LocalDate date, Room roomName, List mailingList) {
+ this.meetingId = meetingId;
+ this.meetingName = meetingName;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.date = date;
+ this.roomName = roomName;
+ this.roomAvatar = avatar;
+ this.mailingList = mailingList;
+
+ }
+
+ public int getMeetingId() {
+ return meetingId;
+ }
+
+ public String getMeetingName() {
+ return meetingName;
+ }
+
+ public LocalTime getMeetingStart() {
+ return startTime;
+ }
+
+ public LocalTime getMeetingEnd() {
+ return endTime;
+ }
+
+ public LocalDate getMeetingDate() { return date; }
+
+ public Room getRoomName() { return roomName; }
+
+ public Room getRoomAvatar() {
+ return roomAvatar;
+ }
+
+ public List getMailingList() {
+ List emailsString = new ArrayList<>();
+
+ for (Email email : mailingList) {
+ emailsString.add(email.getUrl());
+
+ }
+
+ return emailsString;
+
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Meeting meeting = (Meeting) o;
+ return meetingId == meeting.meetingId &&
+ Objects.equals(meetingName, meeting.meetingName) &&
+ Objects.equals(startTime, meeting.startTime) &&
+ Objects.equals(endTime, meeting.endTime) &&
+ Objects.equals(date, meeting.date) &&
+ roomName == meeting.roomName &&
+ roomAvatar == meeting.roomAvatar &&
+ Objects.equals(mailingList, meeting.mailingList);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(meetingId, meetingName, startTime, endTime, date, roomName, roomAvatar, mailingList);
+
+ }
+
+ @Override
+ public String toString() {
+ return "Meeting{" +
+ "meetingId=" + meetingId +
+ ", meetingName='" + meetingName + '\'' +
+ ", startTime='" + startTime + '\'' +
+ ", endTime='" + endTime + '\'' +
+ ", date='" + date + '\'' +
+ ", roomName=" + roomName +
+ ", roomAvatar=" + roomAvatar +
+ ", mailingList=" + mailingList +
+ '}';
+
+ }
+}
+
diff --git a/app/src/main/java/com/kardabel/mareu/model/Room.java b/app/src/main/java/com/kardabel/mareu/model/Room.java
new file mode 100644
index 0000000..807afa0
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/model/Room.java
@@ -0,0 +1,49 @@
+package com.kardabel.mareu.model;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.DrawableRes;
+
+import com.kardabel.mareu.R;
+
+
+public enum Room {
+ ROOM_RESET(R.color.white, "Reset"),
+ ROOM_PEACH(R.color.peach, "Peach"),
+ ROOM_MARIO(R.color.mario, "Mario"),
+ ROOM_LUIGI(R.color.luigi, "Luigi"),
+ ROOM_TOAD(R.color.toad, "Toad"),
+ ROOM_YOSHI(R.color.yoshi, "Yoshi"),
+ ROOM_DONKEY(R.color.donkey, "Donkey"),
+ ROOM_KOOPA(R.color.koopa, "Koopa"),
+ ROOM_BOO(R.color.boo, "Boo"),
+ ROOM_GOOMBA(R.color.goomba, "Goomba"),
+ ROOM_KAMEK(R.color.kamek, "Kamek");
+
+ @ColorRes
+ private final int roomIcon;
+ private final String roomName;
+
+ Room(int roomIcon, String roomName) {
+ this.roomIcon = roomIcon;
+ this.roomName = roomName;
+
+ }
+
+ public int getDrawableRoomIcon(){
+ return roomIcon;
+ }
+
+ public String getRoomMeetingName(){ return roomName; }
+
+ public static Room contains(String addRoomName) {
+ Room room2 = ROOM_RESET;
+
+ for (Room room : Room.values()) {
+ if (room.roomName.equals(addRoomName)) {
+ room2 = room;
+ }
+ }
+ return room2;
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/repository/MeetingsRepository.java b/app/src/main/java/com/kardabel/mareu/repository/MeetingsRepository.java
new file mode 100644
index 0000000..2ec1ef9
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/repository/MeetingsRepository.java
@@ -0,0 +1,58 @@
+package com.kardabel.mareu.repository;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import com.kardabel.mareu.data.FakeDataStore;
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.model.Room;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.Iterator;
+import java.util.List;
+
+
+public class MeetingsRepository {
+
+ private List nMeetings = FakeDataStore.generateMeetingList();
+
+ private List nMeetingsInitial = FakeDataStore.generateMeetingList();
+
+ private final MutableLiveData> mutableMeetingList = new MutableLiveData<>(nMeetings);
+
+ public void addNewMeeting(Meeting meeting){
+ nMeetings.add(meeting);
+ mutableMeetingList.setValue(nMeetings);
+
+ }
+
+ public void deleteMeeting(int meetingId){
+ for (Iterator iterator = nMeetings.iterator(); iterator.hasNext();){
+ Meeting meeting = iterator.next();
+ if(meeting.getMeetingId() == meetingId){
+ iterator.remove();
+
+ }
+ }
+ mutableMeetingList.setValue(nMeetings);
+
+ }
+
+ // Search for the higher Id in current list
+ public int findLastMeetingId(){
+ int size = nMeetings.size() - 1;
+ int id = nMeetings.get(size).getMeetingId();
+ return id;
+
+ }
+
+ public void resetListForNonPersistentData(){
+ nMeetings = nMeetingsInitial;
+ mutableMeetingList.setValue(nMeetings);
+
+ }
+
+ public LiveData> getMeetingsList() { return mutableMeetingList; }
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/DatePickerFragment.java b/app/src/main/java/com/kardabel/mareu/ui/DatePickerFragment.java
new file mode 100644
index 0000000..f17dafb
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/DatePickerFragment.java
@@ -0,0 +1,28 @@
+package com.kardabel.mareu.ui;
+
+import android.app.DatePickerDialog;
+import android.app.Dialog;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+
+import java.time.LocalDate;
+
+
+public class DatePickerFragment extends DialogFragment {
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ LocalDate date = LocalDate.now();
+
+ int year = date.getYear();
+ int month = date.getMonthValue() - 1;
+ int day = date.getDayOfMonth();
+
+ return new DatePickerDialog(getActivity(), (DatePickerDialog.OnDateSetListener) getActivity(), year, month, day);
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/TimePickerFragment.java b/app/src/main/java/com/kardabel/mareu/ui/TimePickerFragment.java
new file mode 100644
index 0000000..d78db3d
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/TimePickerFragment.java
@@ -0,0 +1,38 @@
+package com.kardabel.mareu.ui;
+
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
+
+import java.time.LocalTime;
+
+/**
+ * Created by stéphane Warin OCR on 09/04/2021.
+ */
+public class TimePickerFragment extends DialogFragment {
+
+ public static final int FLAG_START_TIME = 0;
+ public static final int FLAG_END_TIME = 1;
+
+ private int flag = 0;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ LocalTime time = LocalTime.now();
+
+ int hour = time.getHour();
+ int minute = time.getMinute();
+
+ return new TimePickerDialog(getActivity(), (TimePickerDialog.OnTimeSetListener) getActivity(), hour, minute, DateFormat.is24HourFormat(getActivity()));
+
+ }
+ public void setFlag(int i) {
+ flag = i;
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/add/AddMeetingActivity.java b/app/src/main/java/com/kardabel/mareu/ui/add/AddMeetingActivity.java
new file mode 100644
index 0000000..dbaf944
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/add/AddMeetingActivity.java
@@ -0,0 +1,248 @@
+package com.kardabel.mareu.ui.add;
+
+import android.app.DatePickerDialog;
+import android.app.TimePickerDialog;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.os.Bundle;
+import android.util.Patterns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.TimePicker;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.google.android.material.chip.Chip;
+import com.google.android.material.chip.ChipGroup;
+import com.google.android.material.textfield.TextInputLayout;
+import com.kardabel.mareu.R;
+import com.kardabel.mareu.di.MareuViewModelFactory;
+import com.kardabel.mareu.ui.DatePickerFragment;
+import com.kardabel.mareu.ui.TimePickerFragment;
+import com.kardabel.mareu.ui.add.utils.AddMeetingViewAction;
+
+
+public class AddMeetingActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSetListener, DatePickerDialog.OnDateSetListener {
+
+ private TextInputLayout email;
+ private TextInputLayout meetingName;
+ private AddMeetingViewModel addMeetingViewModel;
+ private ChipGroup addMeetingChipGroup;
+ private String meetingRoom;
+ private int flag = -1;
+ private String startTimeText;
+ private String endTimeText;
+ private String dateText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_addmeeting);
+
+ meetingName = findViewById(R.id.meeting_name);
+ addMeetingChipGroup = findViewById(R.id.add_meeting_chipGroup);
+
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.add_meeting_toolbar);
+ toolbar.getNavigationIcon().setColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY);
+
+ addMeetingViewModel =
+ new ViewModelProvider(this, MareuViewModelFactory.getInstance()).get(AddMeetingViewModel.class);
+
+ // Dropdown room choice menu
+ AutoCompleteTextView roomSelect = findViewById(R.id.dropdown_autocomplete);
+ String[] items = new String[]{
+ "Peach", "Mario", "Luigi", "Toad", "Yoshi", "Donkey", "Koopa", "Boo",
+ "Goomba", "Kamek",
+ };
+ ArrayAdapter adapter = new ArrayAdapter<>(
+ AddMeetingActivity.this,
+ R.layout.activity_addmeeting_dropdown_item,
+ items
+ );
+ roomSelect.setAdapter(adapter);
+
+ roomSelect.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ meetingRoom = parent.getItemAtPosition(position).toString();
+
+ }
+ });
+
+ // Date popup
+ EditText dateEditText = findViewById(R.id.date_setter);
+ dateEditText.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ DialogFragment datePicker = new DatePickerFragment();
+ datePicker.show(getSupportFragmentManager(), "date picker");
+ }
+ });
+
+ // Start time popup with flag update
+ EditText startTime = findViewById(R.id.start_time_setter);
+ startTime.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setFlagForTime(0);
+ DialogFragment timePicker = new TimePickerFragment();
+ timePicker.show(getSupportFragmentManager(), "start time picker");
+ }
+ });
+
+ // End time popup with flag update
+ EditText endTime = findViewById(R.id.end_time_setter);
+ endTime.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setFlagForTime(1);
+ DialogFragment timePicker = new TimePickerFragment();
+ timePicker.show(getSupportFragmentManager(), "end time picker");
+ }
+ });
+
+ // Click on "add email" button
+ email = findViewById(R.id.email_input);
+ Button addMailButton = findViewById(R.id.add_mail_button);
+ addMailButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ validateEmailAddress(email);
+ }
+ });
+
+ // Click on "Save" button
+ Button save = findViewById(R.id.create);
+ save.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) { saveMeeting(); }
+ });
+
+ // Back to main activity
+ toolbar.setNavigationOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) { finish(); }
+
+ });
+
+ ///////////////SingleLiveEvent////////////////
+
+ addMeetingViewModel.getViewActionSingleLiveEvent().observe(this, new Observer() {
+ @Override
+ public void onChanged(AddMeetingViewAction action) {
+ switch (action) {
+ case FINISH_ACTIVITY:
+ finish();
+ Toast.makeText(AddMeetingActivity.this, "Thank you !", Toast.LENGTH_LONG).show();
+ break;
+ case DISPLAY_START_HOUR:
+ startTime.setText(startTimeText);
+ break;
+ case DISPLAY_END_HOUR:
+ endTime.setText(endTimeText);
+ break;
+ case DISPLAY_DATE:
+ dateEditText.setText(dateText);
+ break;
+ case DISPLAY_FIELDS_ERROR:
+ Toast.makeText(AddMeetingActivity.this, "Complete all fields please", Toast.LENGTH_LONG).show();
+ break;
+ case DISPLAY_START_OOB:
+ Toast.makeText(AddMeetingActivity.this, "Start time must be between 8am and 17pm", Toast.LENGTH_LONG).show();
+ break;
+ case DISPLAY_END_OOB:
+ Toast.makeText(AddMeetingActivity.this, "End time must before 18pm and after 9am", Toast.LENGTH_LONG).show();
+ break;
+ case DISPLAY_START_AFTER_ERROR:
+ Toast.makeText(AddMeetingActivity.this, "Start time must be before end time !", Toast.LENGTH_LONG).show();
+ break;
+ case DISPLAY_END_BEFORE_ERROR:
+ Toast.makeText(AddMeetingActivity.this, "End time must be after start time !", Toast.LENGTH_LONG).show();
+ break;
+ case DATE_ERROR:
+ Toast.makeText(AddMeetingActivity.this, "Date must be this day or higher", Toast.LENGTH_LONG).show();
+ break;
+ }
+ }
+ });
+ }
+
+ // Flag to know witch time editText to update
+ public void setFlagForTime(int i) {
+ flag = i;
+
+ }
+
+ // Convert date picked to string
+ @Override
+ public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
+ dateText = year + "-" + (month+1) + "-" + dayOfMonth;
+ addMeetingViewModel.onDateSet(year, month, dayOfMonth);
+
+ }
+
+ // Convert time picked to string, then, with flag, we know witch editText to update
+ @Override
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ String min = "";
+ String hour = "";
+ if (minute < 10) { min = "0" + minute; } else { min = String.valueOf(minute); }
+ if (hourOfDay < 10) { hour = "0" + hourOfDay; } else { hour = String.valueOf(hourOfDay); }
+
+ if (flag == 0) {
+ startTimeText = hour + ":" + min;
+ addMeetingViewModel.onStartTimeSet(hourOfDay, minute);
+
+ }
+ if (flag == 1) {
+ endTimeText = hour + ":" + min;
+ addMeetingViewModel.onEndTimeSet(hourOfDay, minute);
+
+ }
+ }
+
+ // Check email entry
+ private boolean validateEmailAddress(TextInputLayout email){
+ String emailInput = email.getEditText().getText().toString();
+ if(!emailInput.isEmpty() && Patterns.EMAIL_ADDRESS.matcher(emailInput).find()){
+ addNewChipEmails(emailInput);
+ addMeetingViewModel.addEmails(emailInput);
+ return true;
+
+ }else{
+ Toast.makeText(this, "invalid address", Toast.LENGTH_SHORT).show();
+ return false;
+
+ }
+ }
+
+ // Add email as a chip in chipGroup
+ private void addNewChipEmails(String email){
+ LayoutInflater inflater = LayoutInflater.from(this);
+ Chip newChip = (Chip) inflater.inflate(R.layout.mail_chip_item, this.addMeetingChipGroup, false);
+ newChip.setText(email);
+ this.addMeetingChipGroup.addView(newChip);
+
+ }
+
+ // When meeting is about to be saved
+ public void saveMeeting() {
+ String stringMeetingName = meetingName.getEditText().getText().toString();
+ addMeetingViewModel.addMeetingName(stringMeetingName);
+ addMeetingViewModel.addMeetingRoom(meetingRoom);
+ addMeetingViewModel.onSaveButtonClick();
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/add/AddMeetingViewModel.java b/app/src/main/java/com/kardabel/mareu/ui/add/AddMeetingViewModel.java
new file mode 100644
index 0000000..8c6a1d8
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/add/AddMeetingViewModel.java
@@ -0,0 +1,153 @@
+package com.kardabel.mareu.ui.add;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModel;
+
+import com.kardabel.mareu.model.Email;
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.model.Room;
+import com.kardabel.mareu.repository.MeetingsRepository;
+import com.kardabel.mareu.ui.add.utils.AddMeetingViewAction;
+import com.kardabel.mareu.ui.add.utils.SingleLiveEvent;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class AddMeetingViewModel extends ViewModel {
+
+ private MeetingsRepository mMeetingsRepository;
+
+ private String mMeetingName;
+ private int meetingId;
+
+ private LocalTime mStartTime;
+ private LocalTime mEndTime;
+ private LocalDate mDate;
+
+ private Room mRoomName;
+ private Room mRoomAvatar;
+
+ private List emails = new ArrayList<>();
+
+
+ private SingleLiveEvent mActionSingleLiveEvent = new SingleLiveEvent<>();
+
+
+ public AddMeetingViewModel(@NonNull MeetingsRepository meetingsRepository) {
+ mMeetingsRepository = meetingsRepository;
+
+ }
+
+ public void addMeetingName(String meetingName) {
+ this.mMeetingName = meetingName;
+
+ }
+
+ public void addMeetingRoom(String roomName) {
+ this.mRoomName = Room.contains(roomName);
+ this.mRoomAvatar = Room.contains(roomName);
+
+ }
+
+ // When start time is picked
+ public void onStartTimeSet(int hourOfDay, int minute) {
+ mStartTime = LocalTime.of(hourOfDay, minute);
+
+ if (mStartTime.isAfter(LocalTime.of(17, 00)) || mStartTime.isBefore(LocalTime.of(8, 00))) {
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_START_OOB);
+ return;
+
+ }
+ if (mEndTime != null) {
+ if (mStartTime.isAfter(mEndTime)) {
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_START_AFTER_ERROR);
+ return;
+
+ }
+ }
+
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_START_HOUR);
+
+ }
+
+ // When end time is picked
+ public void onEndTimeSet(int hourOfDay, int minute) {
+
+ mEndTime = LocalTime.of(hourOfDay, minute);
+
+ if (mEndTime.isAfter(LocalTime.of(18, 00)) || mEndTime.isBefore(LocalTime.of(9, 00))) {
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_END_OOB);
+ return;
+
+ }
+ if (mStartTime != null) {
+ if (mEndTime.isBefore(mStartTime)) {
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_END_BEFORE_ERROR);
+ return;
+
+ }
+ }
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_END_HOUR);
+
+ }
+
+ // When a date is picked
+ public void onDateSet(int year, int month, int dayOfMonth) {
+ mDate = LocalDate.of(year, month+1, dayOfMonth);
+ if(mDate.isBefore(LocalDate.now())){
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DATE_ERROR);
+ return;
+
+ }
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_DATE);
+
+ }
+
+ // Create meeting ID
+ public int meetingId() {
+ int lastmeetingId = mMeetingsRepository.findLastMeetingId();
+ int meetingId = lastmeetingId + 1;
+ return meetingId;
+
+ }
+
+
+ public void addEmails(String email) {
+ emails.add(new Email(email));
+
+ }
+
+ public void onSaveButtonClick() {
+ Meeting meeting = new Meeting(
+ meetingId(),
+ mMeetingName,
+ mRoomAvatar,
+ mStartTime,
+ mEndTime,
+ mDate,
+ mRoomName,
+ emails
+ );
+ if (completeReunion()) {
+ mMeetingsRepository.addNewMeeting(meeting);
+
+ }
+ }
+
+ private boolean completeReunion() {
+ if (mMeetingName != null && mStartTime != null && mEndTime != null && mDate !=null && mRoomName!= null && mRoomAvatar != null && emails!= null) {
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.FINISH_ACTIVITY);
+ return true;
+ }
+ else {
+ mActionSingleLiveEvent.setValue(AddMeetingViewAction.DISPLAY_FIELDS_ERROR);
+ return false;
+ }
+ }
+
+ public SingleLiveEvent getViewActionSingleLiveEvent() {return mActionSingleLiveEvent;}
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/add/utils/AddMeetingViewAction.java b/app/src/main/java/com/kardabel/mareu/ui/add/utils/AddMeetingViewAction.java
new file mode 100644
index 0000000..58b76fe
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/add/utils/AddMeetingViewAction.java
@@ -0,0 +1,20 @@
+package com.kardabel.mareu.ui.add.utils;
+
+
+public enum AddMeetingViewAction {
+
+ // for singleLiveEVent
+ DISPLAY_FIELDS_ERROR,
+ FINISH_ACTIVITY,
+ DISPLAY_END_OOB,
+ DISPLAY_START_OOB,
+ DISPLAY_END_BEFORE_ERROR,
+ DISPLAY_START_AFTER_ERROR,
+ DISPLAY_START_HOUR,
+ DISPLAY_END_HOUR,
+ DISPLAY_DATE,
+ DATE_ERROR
+
+
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/add/utils/SingleLiveEvent.java b/app/src/main/java/com/kardabel/mareu/ui/add/utils/SingleLiveEvent.java
new file mode 100644
index 0000000..4ae2d18
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/add/utils/SingleLiveEvent.java
@@ -0,0 +1,51 @@
+package com.kardabel.mareu.ui.add.utils;
+
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+
+public class SingleLiveEvent extends MutableLiveData {
+
+ private static final String TAG = "SingleLiveEvent";
+
+ private final AtomicBoolean mPending = new AtomicBoolean(false);
+
+ @MainThread
+ public void observeSingleEvent(LifecycleOwner owner, final Observer observer) {
+
+ if (hasActiveObservers()) {
+ Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
+ }
+
+ // Observe the internal MutableLiveData
+ super.observe(owner, new Observer() {
+ @Override
+ public void onChanged(@Nullable T t) {
+ if (mPending.compareAndSet(true, false)) {
+ observer.onChanged(t);
+ }
+ }
+ });
+ }
+
+ @MainThread
+ public void setValue(@Nullable T t) {
+ mPending.set(true);
+ super.setValue(t);
+ }
+
+ /**
+ * Used for cases where T is Void, to make calls cleaner.
+ */
+ @MainThread
+ public void call() {
+ setValue(null);
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/details/DetailsViewModel.java b/app/src/main/java/com/kardabel/mareu/ui/details/DetailsViewModel.java
new file mode 100644
index 0000000..8ebf295
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/details/DetailsViewModel.java
@@ -0,0 +1,65 @@
+package com.kardabel.mareu.ui.details;
+
+import androidx.annotation.NonNull;
+import androidx.arch.core.util.Function;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Transformations;
+import androidx.lifecycle.ViewModel;
+
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.repository.MeetingsRepository;
+
+import java.util.List;
+
+
+public class DetailsViewModel extends ViewModel {
+ private int meetingId = -1;
+
+ private MeetingsRepository mMeetingsRepository;
+ private DetailsViewState result;
+
+ private LiveData meetingsDetailsLiveData;
+
+
+ public DetailsViewModel(@NonNull MeetingsRepository meetingsRepository) {
+ mMeetingsRepository = meetingsRepository;
+ meetingsDetailsLiveData = Transformations.map(mMeetingsRepository.getMeetingsList(), new Function, DetailsViewState>() {
+ @Override
+ public DetailsViewState apply(List meeting) {
+ return map(meeting);
+
+ }
+ });
+ }
+
+ public void init(int meetingId){
+ this.meetingId = meetingId;
+ }
+
+ private DetailsViewState map(List meetings){
+ for (Meeting meeting: meetings) {
+ String humanReadableStartTime = meeting.getMeetingStart().toString();
+ String humanReadableEndTime = meeting.getMeetingEnd().toString();
+ String humanReadableDate = meeting.getMeetingDate().toString();
+
+ if (meetingId == meeting.getMeetingId()) {
+
+ result = new DetailsViewState(
+ meeting.getMeetingId(),
+ meeting.getMeetingName(),
+ humanReadableStartTime + " to " + humanReadableEndTime,
+ humanReadableDate,
+ meeting.getRoomName().getRoomMeetingName(),
+ meeting.getRoomAvatar().getDrawableRoomIcon(),
+ meeting.getMailingList()
+
+ );
+ }
+ }
+ return result;
+
+ }
+
+ public LiveData getDetailsLiveData(){ return meetingsDetailsLiveData; }
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/details/DetailsViewState.java b/app/src/main/java/com/kardabel/mareu/ui/details/DetailsViewState.java
new file mode 100644
index 0000000..6029333
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/details/DetailsViewState.java
@@ -0,0 +1,60 @@
+package com.kardabel.mareu.ui.details;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.DrawableRes;
+import androidx.lifecycle.ViewModel;
+
+import java.util.List;
+
+
+public class DetailsViewState extends ViewModel {
+
+ private final int meetingId;
+ private final String meetingName;
+ private final String startTime;
+ private final String date;
+ private final String roomName;
+ @ColorRes
+ private final int avatarIcon;
+ private final List mailingList;
+
+ public DetailsViewState(int meetingId, String meetingName, String startTime, String date, String roomName, int avatarIcon, List mailingList) {
+ this.meetingId = meetingId;
+ this.meetingName = meetingName;
+ this.startTime = startTime;
+ this.date = date;
+ this.roomName = roomName;
+ this.avatarIcon = avatarIcon;
+ this.mailingList = mailingList;
+
+ }
+
+ public int getDetailsMeetingId() {
+ return meetingId;
+ }
+
+ public String getDetailsMeetingName() {
+ return meetingName;
+ }
+
+ public String getDetailsStartTime() {
+ return startTime;
+ }
+
+ public String getDetailsDate() {
+ return date;
+ }
+
+ public String getDetailsRoomName() {
+ return roomName;
+ }
+
+ public int getDetailsAvatarIcon() {
+ return avatarIcon;
+ }
+
+ public List getDetailsEmails() {
+ return mailingList;
+ }
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/list/DetailsActivity.java b/app/src/main/java/com/kardabel/mareu/ui/list/DetailsActivity.java
new file mode 100644
index 0000000..c20be4e
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/list/DetailsActivity.java
@@ -0,0 +1,95 @@
+package com.kardabel.mareu.ui.list;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.google.android.material.chip.Chip;
+import com.google.android.material.chip.ChipGroup;
+import com.kardabel.mareu.R;
+import com.kardabel.mareu.di.MareuViewModelFactory;
+import com.kardabel.mareu.ui.details.DetailsViewModel;
+import com.kardabel.mareu.ui.details.DetailsViewState;
+
+import java.util.List;
+
+
+public class DetailsActivity extends AppCompatActivity {
+
+ public static final String EXTRA_MEETING_ID = "EXTRA_MEETINGID";
+
+ private TextView meetingName;
+ private TextView startAndEndTime;
+ private TextView date;
+ private TextView roomMeetingName;
+ private ImageView meetingAvatar;
+ private ImageButton backButton;
+
+ private ChipGroup chipGroup;
+
+ private DetailsViewModel mDetailsViewModel;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_meetings_details);
+
+ meetingName = findViewById(R.id.detail_meeting_name);
+ startAndEndTime = findViewById(R.id.detail_hour);
+ date = findViewById(R.id.detail_date);
+ roomMeetingName = findViewById(R.id.detail_room_name);
+ meetingAvatar = findViewById(R.id.detail_room_avatar);
+ backButton = findViewById(R.id.back_button_meeting_details);
+ chipGroup = findViewById(R.id.chipgroup_detail_emails);
+
+
+ mDetailsViewModel =
+ new ViewModelProvider(this, MareuViewModelFactory.getInstance()).get(DetailsViewModel.class);
+
+ Intent intent = getIntent();
+ mDetailsViewModel.init(intent.getIntExtra(EXTRA_MEETING_ID, -1));
+
+
+ mDetailsViewModel.getDetailsLiveData().observe(this, new Observer() {
+ @Override
+ public void onChanged(DetailsViewState meeting) {
+ List emails;
+ meetingName.setText(meeting.getDetailsMeetingName());
+ startAndEndTime.setText(meeting.getDetailsStartTime());
+ date.setText(meeting.getDetailsDate());
+ roomMeetingName.setText(meeting.getDetailsRoomName());
+ meetingAvatar.setImageResource(meeting.getDetailsAvatarIcon());
+ emails = meeting.getDetailsEmails();
+ for(String email : emails){
+ addNewChipEmails(email);
+
+ }
+ }
+ });
+
+ // Back to main activity
+ backButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+
+ });
+ }
+
+ private void addNewChipEmails(String email){
+ LayoutInflater inflater = LayoutInflater.from(this);
+ Chip newChip = (Chip) inflater.inflate(R.layout.mail_chip_item, this.chipGroup, false);
+ newChip.setText(email);
+ this.chipGroup.addView(newChip);
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/list/MainActivity.java b/app/src/main/java/com/kardabel/mareu/ui/list/MainActivity.java
new file mode 100644
index 0000000..9f2d467
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/list/MainActivity.java
@@ -0,0 +1,166 @@
+package com.kardabel.mareu.ui.list;
+
+
+import android.app.DatePickerDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.DatePicker;
+
+import androidx.appcompat.widget.Toolbar;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.DialogFragment;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.kardabel.mareu.R;
+import com.kardabel.mareu.di.MareuViewModelFactory;
+import com.kardabel.mareu.model.Room;
+import com.kardabel.mareu.ui.add.AddMeetingActivity;
+import com.kardabel.mareu.ui.DatePickerFragment;
+
+import java.util.List;
+
+
+public class MainActivity extends AppCompatActivity implements DatePickerDialog.OnDateSetListener {
+
+ private MainViewModel mMainViewModel;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mareu);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ RecyclerView mRecyclerView = findViewById(R.id.recyclerView);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+
+ MeetingsRecyclerViewAdapter mAdapter = new MeetingsRecyclerViewAdapter();
+ mRecyclerView.setAdapter(mAdapter);
+
+ mMainViewModel =
+ new ViewModelProvider(this, MareuViewModelFactory.getInstance()).get(MainViewModel.class);
+
+ mMainViewModel.getMeetingsListLiveData().observe(this, new Observer>() {
+ @Override
+ public void onChanged(List meetings) {
+ mAdapter.setMeetings(meetings);
+
+ }
+ });
+
+ // Launch add meeting activity
+ FloatingActionButton floatingActionButton = findViewById(R.id.add_meeting_button);
+ floatingActionButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(MainActivity.this, AddMeetingActivity.class);
+ startActivity(intent);
+
+ }
+ });
+
+ // Launch details via adapter interface
+ mAdapter.setOnItemClickListener(new MeetingsRecyclerViewAdapter.OnItemClickListener() {
+ @Override
+ public void onMeetingItemClick(MainViewState meeting) {
+ Intent intent = new Intent(MainActivity.this, DetailsActivity.class);
+ intent.putExtra(DetailsActivity.EXTRA_MEETING_ID, meeting.getMeetingId());
+ startActivity(intent);
+
+ }
+
+ // Delete button via adapter interface
+ @Override
+ public void onDeleteMeetingClick(int meeting) {
+ mMainViewModel.deleteMeeting(meeting);
+
+ }
+ });
+ }
+
+ // TopBar overflow for filters dropdown menu
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.top_app_bar, menu);
+ return true;
+
+ }
+
+ // Room filters action
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.reset_filter:
+ mMainViewModel.resetFilters();
+ return true;
+ case R.id.date_filter:
+ DialogFragment datePicker = new DatePickerFragment();
+ datePicker.show(getSupportFragmentManager(), "filter date picker");
+ return true;
+ case R.id.peach:
+ mMainViewModel.roomFilterValue(Room.ROOM_PEACH);
+ return true;
+ case R.id.mario:
+ mMainViewModel.roomFilterValue(Room.ROOM_MARIO);
+ return true;
+ case R.id.luigi:
+ mMainViewModel.roomFilterValue(Room.ROOM_LUIGI);
+ return true;
+ case R.id.toad:
+ mMainViewModel.roomFilterValue(Room.ROOM_TOAD);
+ return true;
+ case R.id.yoshi:
+ mMainViewModel.roomFilterValue(Room.ROOM_YOSHI);
+ return true;
+ case R.id.donkey:
+ mMainViewModel.roomFilterValue(Room.ROOM_DONKEY);
+ return true;
+ case R.id.koopa:
+ mMainViewModel.roomFilterValue(Room.ROOM_KOOPA);
+ return true;
+ case R.id.boo:
+ mMainViewModel.roomFilterValue(Room.ROOM_BOO);
+ return true;
+ case R.id.goomba:
+ mMainViewModel.roomFilterValue(Room.ROOM_GOOMBA);
+ return true;
+ case R.id.kamek:
+ mMainViewModel.roomFilterValue(Room.ROOM_KAMEK);
+ return true;
+ case R.id.allMeeting:
+ mMainViewModel.roomFilterValue(Room.ROOM_RESET);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+
+ }
+ }
+
+ // Date filter action
+ @Override
+ public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
+ mMainViewModel.onDateFilterPicked(year, month, dayOfMonth);
+
+ }
+
+ // Back to initial list
+ @Override
+ protected void onDestroy() {
+ mMainViewModel.resetList();
+ super.onDestroy();
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/list/MainViewModel.java b/app/src/main/java/com/kardabel/mareu/ui/list/MainViewModel.java
new file mode 100644
index 0000000..fb32518
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/list/MainViewModel.java
@@ -0,0 +1,154 @@
+package com.kardabel.mareu.ui.list;
+
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.ViewModel;
+
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.model.Room;
+import com.kardabel.mareu.repository.MeetingsRepository;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class MainViewModel extends ViewModel {
+
+ private final MeetingsRepository mMeetingsRepository;
+
+ private final MediatorLiveData> meetingsListMediatorLiveData = new MediatorLiveData<>();
+
+ private final MutableLiveData roomFilterMutableLiveData = new MutableLiveData<>();
+ private final MutableLiveData dateFilterMutableLiveData = new MutableLiveData<>();
+
+ public MainViewModel(@NonNull MeetingsRepository meetingsRepository) {
+ mMeetingsRepository = meetingsRepository;
+
+ LiveData> meetingsListLiveData = mMeetingsRepository.getMeetingsList();
+
+ meetingsListMediatorLiveData.addSource(meetingsListLiveData, new Observer>() {
+ @Override
+ public void onChanged(List meetings) {
+ combine(meetings, roomFilterMutableLiveData.getValue(), dateFilterMutableLiveData.getValue());
+
+ }
+ });
+
+ meetingsListMediatorLiveData.addSource(roomFilterMutableLiveData, new Observer() {
+ @Override
+ public void onChanged(Room room) {
+ combine(meetingsListLiveData.getValue(), room, dateFilterMutableLiveData.getValue());
+
+ }
+ });
+
+ meetingsListMediatorLiveData.addSource(dateFilterMutableLiveData, new Observer() {
+ @Override
+ public void onChanged(LocalDate Date) {
+ combine(meetingsListLiveData.getValue(), roomFilterMutableLiveData.getValue(), Date);
+
+ }
+ });
+ }
+
+ // Filter the Mediator
+ private void combine(@Nullable List meetings, @Nullable Room room, @Nullable LocalDate date) {
+ List filteredMeetings = new ArrayList<>();
+ if((room == null) && (date == null)){
+ meetingsListMediatorLiveData.setValue(map(meetings));
+
+ }
+ else{
+ for (Meeting meeting : meetings) {
+ if(meeting.getRoomName().equals(room) || meeting.getMeetingDate().equals(date)){
+ filteredMeetings.add(meeting);
+
+ }
+ }
+ meetingsListMediatorLiveData.setValue(map(filteredMeetings));
+ }
+ }
+
+ //Create meeting view state
+ private List map(List meetings){
+ List result = new ArrayList<>();
+
+ for (Meeting meeting: meetings) {
+ String humanReadableHour = meeting.getMeetingStart().toString();
+ String humanReadableDate = meeting.getMeetingDate().toString();
+ String humanReadableEmails = readableEmails(meeting);
+
+ result.add(new MainViewState(
+ meeting.getMeetingId(),
+ humanReadableDate,
+ meeting.getMeetingName() + " - " + humanReadableHour + " - " + meeting.getRoomName().getRoomMeetingName(),
+ meeting.getRoomName().getRoomMeetingName(),
+ meeting.getRoomAvatar().getDrawableRoomIcon(),
+ humanReadableEmails
+
+ ));
+ }
+ return result;
+ }
+
+ // Convert emails
+ private String readableEmails(Meeting meeting){
+ String humanReadableEmails;
+ List emails = meeting.getMailingList();
+ String spaceDelimitation = " - ";
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ while ( i < emails.size() - 1){
+ sb.append(emails.get(i));
+ sb.append(spaceDelimitation);
+ i++;
+
+ }
+ sb.append(emails.get(i));
+ humanReadableEmails = sb.toString();
+ return humanReadableEmails;
+
+ }
+
+ public void roomFilterValue(Room room){
+ roomFilterMutableLiveData.setValue(room);
+ dateFilterMutableLiveData.setValue(null);
+ }
+
+ public void onDateFilterPicked(int year, int month, int dayOfMonth) {
+ LocalDate date = LocalDate.of(year, month+1, dayOfMonth);
+ dateFilterValue(date);
+
+ }
+
+ public void dateFilterValue(LocalDate date){
+ dateFilterMutableLiveData.setValue(date);
+ roomFilterMutableLiveData.setValue(null);
+
+ }
+
+ public void resetFilters(){
+ dateFilterMutableLiveData.setValue(null);
+ roomFilterMutableLiveData.setValue(null);
+
+ }
+
+ public void deleteMeeting(int meetingId){
+ mMeetingsRepository.deleteMeeting(meetingId);
+
+ }
+
+
+ public void resetList(){
+ mMeetingsRepository.resetListForNonPersistentData();
+ }
+
+ public LiveData> getMeetingsListLiveData(){ return meetingsListMediatorLiveData; }
+
+}
diff --git a/app/src/main/java/com/kardabel/mareu/ui/list/MainViewState.java b/app/src/main/java/com/kardabel/mareu/ui/list/MainViewState.java
new file mode 100644
index 0000000..e2effd1
--- /dev/null
+++ b/app/src/main/java/com/kardabel/mareu/ui/list/MainViewState.java
@@ -0,0 +1,69 @@
+package com.kardabel.mareu.ui.list;
+
+import androidx.annotation.ColorRes;
+import androidx.annotation.DrawableRes;
+
+import java.util.Objects;
+
+
+public class MainViewState {
+ private final int meetingId;
+ private final String meetingDate;
+ private final String meetingDetails;
+ private final String roomName;
+ @ColorRes
+ private final int avatarIcon;
+ private final String mailingList;
+
+
+ public MainViewState(int meetingId, String meetingDate, String meetingDetails, String roomName, @ColorRes int avatarIcon, String mailingList) {
+ this.meetingId = meetingId;
+ this.meetingDate = meetingDate;
+ this.meetingDetails = meetingDetails;
+ this.roomName = roomName;
+ this.avatarIcon = avatarIcon;
+ this.mailingList = mailingList;
+
+ }
+
+ public int getMeetingId(){ return meetingId; }
+
+ public String getMeetingDetailsToDisplay(){ return meetingDetails; }
+
+ public int getAvatarToDisplay(){ return avatarIcon; }
+
+ public String getMailingListToDisplay(){ return mailingList; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MainViewState that = (MainViewState) o;
+ return meetingId == that.meetingId &&
+ avatarIcon == that.avatarIcon &&
+ Objects.equals(meetingDate, that.meetingDate) &&
+ Objects.equals(meetingDetails, that.meetingDetails) &&
+ Objects.equals(roomName, that.roomName) &&
+ Objects.equals(mailingList, that.mailingList);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(meetingId, meetingDate, meetingDetails, roomName, avatarIcon, mailingList);
+
+ }
+
+ @Override
+ public String toString() {
+ return "MainViewState{" +
+ "meetingId=" + meetingId +
+ ", meetingDate='" + meetingDate + '\'' +
+ ", meetingDetails='" + meetingDetails + '\'' +
+ ", roomName='" + roomName + '\'' +
+ ", avatarIcon=" + avatarIcon +
+ ", mailingList='" + mailingList + '\'' +
+ '}';
+
+ }
+}
diff --git a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsRecyclerViewAdapter.java b/app/src/main/java/com/kardabel/mareu/ui/list/MeetingsRecyclerViewAdapter.java
similarity index 55%
rename from app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsRecyclerViewAdapter.java
rename to app/src/main/java/com/kardabel/mareu/ui/list/MeetingsRecyclerViewAdapter.java
index 4e2412d..024fe45 100644
--- a/app/src/main/java/com/kardabel/mareu/mareu/views/MeetingsRecyclerViewAdapter.java
+++ b/app/src/main/java/com/kardabel/mareu/ui/list/MeetingsRecyclerViewAdapter.java
@@ -1,37 +1,31 @@
-package com.kardabel.mareu.mareu.views;
+package com.kardabel.mareu.ui.list;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.kardabel.mareu.R;
-import com.kardabel.mareu.mareu.model.Meeting;
import java.util.ArrayList;
import java.util.List;
-/**
- * Created by stéphane Warin OCR on 26/03/2021.
- */
+
public class MeetingsRecyclerViewAdapter extends RecyclerView.Adapter {
- private List meetings = new ArrayList<>();
+ private List meetings = new ArrayList<>();
private OnItemClickListener listener;
-
-
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.activity_mareu_item, parent, false);
return new ViewHolder(itemView);
@@ -40,62 +34,67 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@Override
public void onBindViewHolder(@NonNull MeetingsRecyclerViewAdapter.ViewHolder holder, int position) {
+ MainViewState meeting = meetings.get(position);
+
+ holder.meetingDetails.setText(meeting.getMeetingDetailsToDisplay());
+ holder.mailingList.setText(meeting.getMailingListToDisplay());
+ holder.roomAvatar.setImageResource(meeting.getAvatarToDisplay());
+
- Meeting meeting = meetings.get(position);
- holder.meetingDetails.setText(meeting.getMeetingDetails());
- holder.mailingList.setText(meeting.getMailingList());
- Glide.with(holder.roomAvatar.getContext())
- .load(meeting.getRoomAvatar())
- .apply(RequestOptions.circleCropTransform())
- .into(holder.roomAvatar);
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ listener.onMeetingItemClick(meeting);
+ }
+ });
+
+ holder.deleteMeetingButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ listener.onDeleteMeetingClick(meeting.getMeetingId());
+
+ }
+ });
}
@Override
public int getItemCount() {
- return meetings.size();
+ if (meetings == null){ return 0; }
+ else {return meetings.size();}
+
}
- public void setMeetings(List meetings){
+ public void setMeetings(List meetings){
this.meetings = meetings;
notifyDataSetChanged();
+
}
+ public void setOnItemClickListener(OnItemClickListener listener){
+ this.listener = listener;
+
+ }
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView roomAvatar;
private TextView meetingDetails;
private TextView mailingList;
+ private ImageButton deleteMeetingButton;
public ViewHolder(@NonNull View itemView) {
super(itemView);
roomAvatar = itemView.findViewById(R.id.room_avatar);
meetingDetails = itemView.findViewById(R.id.meeting_details);
mailingList = itemView.findViewById(R.id.mailing_list);
+ deleteMeetingButton = itemView.findViewById(R.id.delete_button);
}
}
public interface OnItemClickListener{
- void onMeetingItemClick(Meeting meeting);
-
- }
-
- private static class MainDiffCallback extends DiffUtil.ItemCallback {
+ void onMeetingItemClick(MainViewState meeting);
+ void onDeleteMeetingClick(int meeting);
- @Override
- public boolean areItemsTheSame(@NonNull Meeting oldItem, @NonNull Meeting newItem) {
- return oldItem.getMeetingId() == newItem.getMeetingId();
- }
-
- @Override
- public boolean areContentsTheSame(@NonNull Meeting oldItem, @NonNull Meeting newItem) {
- return oldItem.getMeetingDetails().equals(newItem.getMeetingDetails())
- && oldItem.getMailingList().equals(newItem.getMailingList())
- && oldItem.getRoomAvatar() == newItem.getRoomAvatar();
- }
- }
- public void setOnItemClickListener(OnItemClickListener listener){
- this.listener = listener;
}
}
diff --git a/app/src/main/res/drawable/blank.xml b/app/src/main/res/drawable/blank.xml
new file mode 100644
index 0000000..31e7df2
--- /dev/null
+++ b/app/src/main/res/drawable/blank.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/boo.png b/app/src/main/res/drawable/boo.png
new file mode 100644
index 0000000..7a20b24
Binary files /dev/null and b/app/src/main/res/drawable/boo.png differ
diff --git a/app/src/main/res/drawable/circle_avatar.xml b/app/src/main/res/drawable/circle_avatar.xml
new file mode 100644
index 0000000..eef56e2
--- /dev/null
+++ b/app/src/main/res/drawable/circle_avatar.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/donkey.png b/app/src/main/res/drawable/donkey.png
new file mode 100644
index 0000000..3d0d181
Binary files /dev/null and b/app/src/main/res/drawable/donkey.png differ
diff --git a/app/src/main/res/drawable/goomba.png b/app/src/main/res/drawable/goomba.png
new file mode 100644
index 0000000..6cb9dee
Binary files /dev/null and b/app/src/main/res/drawable/goomba.png differ
diff --git a/app/src/main/res/drawable/ic_baseline_alternate_email_24.xml b/app/src/main/res/drawable/ic_baseline_alternate_email_24.xml
new file mode 100644
index 0000000..e6182db
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_alternate_email_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
new file mode 100644
index 0000000..31e7df2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_save_24.xml b/app/src/main/res/drawable/ic_baseline_save_24.xml
new file mode 100644
index 0000000..999020d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_save_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/kamek.png b/app/src/main/res/drawable/kamek.png
new file mode 100644
index 0000000..36c349f
Binary files /dev/null and b/app/src/main/res/drawable/kamek.png differ
diff --git a/app/src/main/res/drawable/koopa.png b/app/src/main/res/drawable/koopa.png
new file mode 100644
index 0000000..43265fb
Binary files /dev/null and b/app/src/main/res/drawable/koopa.png differ
diff --git a/app/src/main/res/drawable/luigi.png b/app/src/main/res/drawable/luigi.png
new file mode 100644
index 0000000..8d4325c
Binary files /dev/null and b/app/src/main/res/drawable/luigi.png differ
diff --git a/app/src/main/res/drawable/mario.png b/app/src/main/res/drawable/mario.png
new file mode 100644
index 0000000..2db2838
Binary files /dev/null and b/app/src/main/res/drawable/mario.png differ
diff --git a/app/src/main/res/drawable/peach.png b/app/src/main/res/drawable/peach.png
new file mode 100644
index 0000000..6f918fb
Binary files /dev/null and b/app/src/main/res/drawable/peach.png differ
diff --git a/app/src/main/res/drawable/toad.png b/app/src/main/res/drawable/toad.png
new file mode 100644
index 0000000..298090f
Binary files /dev/null and b/app/src/main/res/drawable/toad.png differ
diff --git a/app/src/main/res/drawable/yoshi.png b/app/src/main/res/drawable/yoshi.png
new file mode 100644
index 0000000..4752fa2
Binary files /dev/null and b/app/src/main/res/drawable/yoshi.png differ
diff --git a/app/src/main/res/layout/activity_addmeeting.xml b/app/src/main/res/layout/activity_addmeeting.xml
index 6dc0655..be35e04 100644
--- a/app/src/main/res/layout/activity_addmeeting.xml
+++ b/app/src/main/res/layout/activity_addmeeting.xml
@@ -6,15 +6,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80F4F1F1"
+ android:backgroundTint="#FFFFFF"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp">
+ tools:context=".ui.add.AddMeetingActivity">
-
-
+
+
@@ -67,7 +74,7 @@
+ android:backgroundTint="#FFFFFF"
+ android:inputType="none">
+ app:layout_constraintTop_toBottomOf="@+id/room_dropdown_menu" />
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/start_time_setter"
+ app:startIconDrawable="@drawable/ic_baseline_alternate_email_24">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_mareu.xml b/app/src/main/res/layout/activity_mareu.xml
index ca60c9e..99a9b4a 100644
--- a/app/src/main/res/layout/activity_mareu.xml
+++ b/app/src/main/res/layout/activity_mareu.xml
@@ -1,28 +1,32 @@
+ tools:context=".ui.list.MainActivity">
-
+ android:theme="@style/ThemeOverlay.AppCompat.ActionBar">
+
+
-
@@ -40,15 +44,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
- android:layout_margin="16dp"
+ android:layout_marginBottom="40dp"
+ android:layout_marginEnd="8dp"
android:clickable="true"
app:backgroundTint="#EF3A3A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.954"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.976"
app:srcCompat="@drawable/ic_baseline_add_24"
app:tint="#FFFFFF" />
diff --git a/app/src/main/res/layout/activity_mareu_item.xml b/app/src/main/res/layout/activity_mareu_item.xml
index 7c8f2a0..c6307cb 100644
--- a/app/src/main/res/layout/activity_mareu_item.xml
+++ b/app/src/main/res/layout/activity_mareu_item.xml
@@ -2,6 +2,7 @@
-
+ app:layout_constraintTop_toTopOf="parent"
+ android:src="@drawable/circle_avatar" />
+
+ app:layout_constraintTop_toBottomOf="@+id/meeting_details" />
diff --git a/app/src/main/res/layout/activity_meetings_details.xml b/app/src/main/res/layout/activity_meetings_details.xml
index 248add6..0ba70c8 100644
--- a/app/src/main/res/layout/activity_meetings_details.xml
+++ b/app/src/main/res/layout/activity_meetings_details.xml
@@ -1,7 +1,275 @@
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/mail_chip_item.xml b/app/src/main/res/layout/mail_chip_item.xml
new file mode 100644
index 0000000..2247efb
--- /dev/null
+++ b/app/src/main/res/layout/mail_chip_item.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/top_app_bar.xml b/app/src/main/res/menu/top_app_bar.xml
index 1d5091f..bdf79ef 100644
--- a/app/src/main/res/menu/top_app_bar.xml
+++ b/app/src/main/res/menu/top_app_bar.xml
@@ -1,12 +1,122 @@
-
+ app:showAsAction="ifRoom">
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127..0a6a507 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,16 @@
#FF018786
#FF000000
#FFFFFFFF
+ #FFE0E689
+ #FF6FB6B8
+ #FFB892CF
+ #FFCFB992
+ #FFCFB992
+ #FFCF9292
+ #FF606FD5
+ #FF7F60D5
+ #FFA460D5
+ #FFD56060
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e31c764..38b27cd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,7 @@
Maréu
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/kardabel/mareu/ExampleUnitTest.java b/app/src/test/java/com/kardabel/mareu/ExampleUnitTest.java
deleted file mode 100644
index b3a7904..0000000
--- a/app/src/test/java/com/kardabel/mareu/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.kardabel.mareu;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/app/src/test/java/com/kardabel/mareu/ui/list/AddMeetingViewModelTest.java b/app/src/test/java/com/kardabel/mareu/ui/list/AddMeetingViewModelTest.java
new file mode 100644
index 0000000..febc640
--- /dev/null
+++ b/app/src/test/java/com/kardabel/mareu/ui/list/AddMeetingViewModelTest.java
@@ -0,0 +1,155 @@
+package com.kardabel.mareu.ui.list;
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
+
+import com.kardabel.mareu.repository.MeetingsRepository;
+import com.kardabel.mareu.ui.add.utils.AddMeetingViewAction;
+import com.kardabel.mareu.ui.add.AddMeetingViewModel;
+import com.kardabel.mareu.ui.list.utils.LiveDataTestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class AddMeetingViewModelTest {
+
+ private AddMeetingViewModel mAddMeetingViewModel;
+
+ @Rule
+ public final InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
+ private final MeetingsRepository mMeetingsRepository = Mockito.mock(MeetingsRepository.class);
+
+ @Before
+ public void setUp() {
+ mAddMeetingViewModel = new AddMeetingViewModel(mMeetingsRepository);
+ }
+
+ @Test
+ public void picked_startTime_in_right_range_should_display_startTime() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onStartTimeSet(10,55);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_START_HOUR, result);
+
+ }
+
+ @Test
+ public void picked_endTime_in_right_range_should_display_startTime() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onEndTimeSet(15,32);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_END_HOUR, result);
+
+ }
+
+ @Test
+ public void picked_startTime_out_of_range_should_display_toast_error() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onStartTimeSet(07,05);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_START_OOB, result);
+
+ }
+
+ @Test
+ public void picked_endTime_out_of_range_should_display_toast_error() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onEndTimeSet(22, 10);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_END_OOB, result);
+
+ }
+
+ @Test
+ public void picked_startTime_after_endTime_should_display_error() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onEndTimeSet(15,00);
+ mAddMeetingViewModel.onStartTimeSet(15,01);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_START_AFTER_ERROR, result);
+
+ }
+
+ @Test
+ public void picked_endTime_before_startTime_should_display_error() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onStartTimeSet(16,50);
+ mAddMeetingViewModel.onEndTimeSet(16,28);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_END_BEFORE_ERROR, result);
+
+ }
+
+ @Test
+ public void picked_date_in_right_range_should_display_date() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onDateSet(2021, 12, 25);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DISPLAY_DATE, result);
+
+ }
+
+ @Test
+ public void picked_date_out_of_range_should_display_error() throws InterruptedException {
+ // When
+ mAddMeetingViewModel.onDateSet(2020, 12, 25);
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+ // Then
+ assertEquals(AddMeetingViewAction.DATE_ERROR, result);
+
+ }
+
+ @Test
+ public void on_save_click_button_if_complete_meeting_should_add_new_meeting() throws InterruptedException {
+ // Feed the fields
+ mAddMeetingViewModel.addMeetingName("Reunion A");
+ mAddMeetingViewModel.addMeetingRoom("Peach");
+ mAddMeetingViewModel.onStartTimeSet(15, 5);
+ mAddMeetingViewModel.onEndTimeSet(17, 25);
+ mAddMeetingViewModel.onDateSet(2021,05,23);
+ mAddMeetingViewModel.addEmails("alloa@test.mail");
+
+ mAddMeetingViewModel.onSaveButtonClick();
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+
+ assertEquals(AddMeetingViewAction.FINISH_ACTIVITY, result);
+
+ }
+
+ @Test
+ public void on_save_click_button_if_incomplete_meeting_should_display_error() throws InterruptedException {
+ // Feed the fields
+ mAddMeetingViewModel.addMeetingRoom("Peach");
+ mAddMeetingViewModel.onStartTimeSet(15, 5);
+ mAddMeetingViewModel.onEndTimeSet(17, 25);
+ mAddMeetingViewModel.onDateSet(2021,05,23);
+ mAddMeetingViewModel.addEmails("alloa@test.mail");
+
+ mAddMeetingViewModel.onSaveButtonClick();
+
+ AddMeetingViewAction result = LiveDataTestUtils.getOrAwaitValue(mAddMeetingViewModel.getViewActionSingleLiveEvent());
+
+ assertEquals(AddMeetingViewAction.DISPLAY_FIELDS_ERROR, result);
+
+ }
+}
diff --git a/app/src/test/java/com/kardabel/mareu/ui/list/DetailsViewModelTest.java b/app/src/test/java/com/kardabel/mareu/ui/list/DetailsViewModelTest.java
new file mode 100644
index 0000000..2e3d237
--- /dev/null
+++ b/app/src/test/java/com/kardabel/mareu/ui/list/DetailsViewModelTest.java
@@ -0,0 +1,227 @@
+package com.kardabel.mareu.ui.list;
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
+import androidx.lifecycle.MutableLiveData;
+
+import com.kardabel.mareu.model.Email;
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.model.Room;
+import com.kardabel.mareu.repository.MeetingsRepository;
+import com.kardabel.mareu.ui.details.DetailsViewModel;
+import com.kardabel.mareu.ui.details.DetailsViewState;
+import com.kardabel.mareu.ui.list.utils.LiveDataTestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+
+public class DetailsViewModelTest {
+
+ @Rule
+ public final InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
+
+ private final MeetingsRepository mMeetingsRepository = Mockito.mock(MeetingsRepository.class);
+
+ private final MutableLiveData> meetingsMutableLiveData = new MutableLiveData<>();
+
+ private DetailsViewModel mDetailsViewModel;
+
+ @Before
+ public void setUp() {
+ Mockito.doReturn(meetingsMutableLiveData).when(mMeetingsRepository).getMeetingsList();
+
+ mDetailsViewModel = new DetailsViewModel(mMeetingsRepository);
+ }
+
+ @Test
+ public void nominal_case() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals(result, null);
+
+ }
+
+ @Test
+ public void given_meeting_id_is_0_should_display_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(0);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( 0, result.getDetailsMeetingId());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_2_should_display_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(2);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( 2, result.getDetailsMeetingId());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_3_should_display_string_hour() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(3);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( "16:50 to 15:10", result.getDetailsStartTime());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_2_should_display_string_date() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(2);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( "2021-06-15", result.getDetailsDate());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_0_should_display_room_name() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(0);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( "Mario", result.getDetailsRoomName());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_2_should_display_room_name() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(2);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( "Goomba", result.getDetailsRoomName());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_3_should_display_meeting_name() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(3);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( "Reunion D", result.getDetailsMeetingName());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_1_should_display_meeting_name() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(1);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( "Reunion B", result.getDetailsMeetingName());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_2_should_display_2_chip() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(2);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( Arrays.asList(
+ ("philibert@monmail.fr"),
+ ("peteretsteven@monmail.fr")),
+ result.getDetailsEmails());
+
+ }
+
+ @Test
+ public void given_meeting_id_is_2_should_display_3_chip() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mDetailsViewModel.init(3);
+ DetailsViewState result = LiveDataTestUtils.getOrAwaitValue(mDetailsViewModel.getDetailsLiveData());
+
+ // Then
+ assertEquals( Arrays.asList(
+ ("krabulbe@monmail.fr"),
+ ("peteretsteven@monmail.fr"),
+ ("unclebob@monmail.fr")),
+ result.getDetailsEmails());
+
+ }
+
+ private List getDefaultMeetings() {
+ return Arrays.asList(
+ new Meeting(0, "Reunion A", Room.ROOM_MARIO, LocalTime.of(15, 10), LocalTime.of(15, 10),LocalDate.of(2021, 05, 12), Room.ROOM_MARIO, Arrays.asList(
+ new Email("stephane@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(1, "Reunion B", Room.ROOM_PEACH, LocalTime.of(9, 00), LocalTime.of(15, 10),LocalDate.of(2021, 11, 02), Room.ROOM_PEACH, Arrays.asList(
+ new Email("warin@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(2, "Reunion c", Room.ROOM_GOOMBA, LocalTime.of(11, 8), LocalTime.of(15, 10),LocalDate.of(2021, 06, 15), Room.ROOM_GOOMBA, Arrays.asList(
+ new Email("philibert@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(3, "Reunion D", Room.ROOM_BOO, LocalTime.of(16, 50), LocalTime.of(15, 10),LocalDate.of(2021, 10, 02), Room.ROOM_BOO, Arrays.asList(
+ new Email("krabulbe@monmail.fr"),
+ new Email("peteretsteven@monmail.fr"),
+ new Email("unclebob@monmail.fr")
+
+ ))
+ );
+ }
+}
diff --git a/app/src/test/java/com/kardabel/mareu/ui/list/MainViewModelTest.java b/app/src/test/java/com/kardabel/mareu/ui/list/MainViewModelTest.java
new file mode 100644
index 0000000..e37cbc1
--- /dev/null
+++ b/app/src/test/java/com/kardabel/mareu/ui/list/MainViewModelTest.java
@@ -0,0 +1,255 @@
+package com.kardabel.mareu.ui.list;
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule;
+import androidx.lifecycle.MutableLiveData;
+
+import com.kardabel.mareu.R;
+import com.kardabel.mareu.model.Email;
+import com.kardabel.mareu.model.Meeting;
+import com.kardabel.mareu.model.Room;
+import com.kardabel.mareu.repository.MeetingsRepository;
+import com.kardabel.mareu.ui.list.utils.LiveDataTestUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+
+
+public class MainViewModelTest {
+
+ @Rule
+ public final InstantTaskExecutorRule mInstantTaskExecutorRule = new InstantTaskExecutorRule();
+
+ private final MeetingsRepository mMeetingsRepository = Mockito.mock(MeetingsRepository.class);
+
+ private final MutableLiveData> meetingsMutableLiveData = new MutableLiveData<>();
+
+ private MainViewModel mMainViewModel;
+
+ @Before
+ public void setUp() {
+ Mockito.doReturn(meetingsMutableLiveData).when(mMeetingsRepository).getMeetingsList();
+ mMainViewModel = new MainViewModel(mMeetingsRepository);
+
+ }
+
+ @Test
+ public void nominal_case() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+
+ // Then
+ assertEquals(result, getDefaultMainViewState());
+
+ }
+
+ @Test
+ public void given_0_meeting_should_display_empty_state() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(new ArrayList<>());
+
+ // When
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(0, result.size());
+
+ }
+
+ @Test
+ public void given_3_meetings_should_display_3_meetings() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getThreeMeetings());
+
+ // When
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(3, result.size());
+
+ }
+
+
+ @Test
+ public void given_room_filter_is_mario_should_display_1_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.roomFilterValue(Room.ROOM_MARIO);
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(1, result.size());
+ MainViewState firstResult = result.get(0);
+ assertEquals(new MainViewState(0, "2021-05-12", "Reunion A - 15:10 - Mario", "Mario", R.color.mario, "stephane@monmail.fr - peteretsteven@monmail.fr"), firstResult);
+
+ }
+
+ @Test
+ public void given_date_filter_is_out_of_all_meetings_should_display_empty_list() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.dateFilterValue(LocalDate.of(2020, 05, 05));
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(0, result.size());
+
+ }
+
+ @Test
+ public void given_date_filter_is_meeting_id_0_should_display_1_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.dateFilterValue(LocalDate.of(2021, 05, 12));
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(1, result.size());
+
+ }
+
+ @Test
+ public void given_date_filter_is_meeting_id_1_should_display_1_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.dateFilterValue(LocalDate.of(2021, 11, 02));
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(1, result.size());
+
+ }
+
+ @Test
+ public void given_reset_filter_after_date_filter_should_display_4_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.dateFilterValue(LocalDate.of(2021, 05, 12));
+ mMainViewModel.resetFilters();
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(4, result.size());
+
+ }
+
+ @Test
+ public void given_reset_filter_after_room_filter_should_display_4_meeting() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.roomFilterValue(Room.ROOM_BOO);
+ mMainViewModel.resetFilters();
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(4, result.size());
+
+ }
+
+ @Test
+ public void given_date_and_room_filters_then_reset_should_display_all_meetings() throws InterruptedException {
+ // Given
+ meetingsMutableLiveData.setValue(getDefaultMeetings());
+
+ // When
+ mMainViewModel.dateFilterValue(LocalDate.of(2021, 11, 02));
+ mMainViewModel.roomFilterValue(Room.ROOM_GOOMBA);
+ mMainViewModel.resetFilters();
+ List result = LiveDataTestUtils.getOrAwaitValue(mMainViewModel.getMeetingsListLiveData());
+
+ // Then
+ assertEquals(4, result.size());
+
+ }
+
+ @Test
+ public void given_meeting_to_delete_should_display_3_meetings() throws InterruptedException {
+
+ // When
+ mMainViewModel.deleteMeeting(0);
+
+ // Then
+ Mockito.verify(mMeetingsRepository).deleteMeeting(0);
+
+ }
+
+ // IN
+ private List getDefaultMeetings() {
+ return Arrays.asList(
+ new Meeting(0, "Reunion A", Room.ROOM_MARIO, LocalTime.of(15, 10), LocalTime.of(16, 50), LocalDate.of(2021, 05, 12), Room.ROOM_MARIO, Arrays.asList(
+ new Email("stephane@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(1, "Reunion B", Room.ROOM_PEACH, LocalTime.of(9, 00), LocalTime.of(11, 8), LocalDate.of(2021, 11, 02), Room.ROOM_PEACH, Arrays.asList(
+ new Email("warin@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(2, "Reunion C", Room.ROOM_GOOMBA, LocalTime.of(11, 8), LocalTime.of(12, 35), LocalDate.of(2021, 06, 15), Room.ROOM_GOOMBA, Arrays.asList(
+ new Email("philibert@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(3, "Reunion D", Room.ROOM_BOO, LocalTime.of(16, 50), LocalTime.of(17, 58), LocalDate.of(2021, 10, 02), Room.ROOM_BOO, Arrays.asList(
+ new Email("krabulbe@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ ))
+ );
+ }
+
+ private List getThreeMeetings() {
+ return Arrays.asList(
+ new Meeting(0, "Reunion A", Room.ROOM_MARIO, LocalTime.of(15, 10), LocalTime.of(16, 50), LocalDate.of(2021, 05, 12), Room.ROOM_MARIO, Arrays.asList(
+ new Email("stephane@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(1, "Reunion B", Room.ROOM_PEACH, LocalTime.of(9, 00), LocalTime.of(11, 8), LocalDate.of(2021, 05, 12), Room.ROOM_PEACH, Arrays.asList(
+ new Email("warin@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ )),
+ new Meeting(2, "Reunion c", Room.ROOM_GOOMBA, LocalTime.of(11, 8), LocalTime.of(17, 58), LocalDate.of(2021, 06, 15), Room.ROOM_GOOMBA, Arrays.asList(
+ new Email("philibert@monmail.fr"),
+ new Email("peteretsteven@monmail.fr")
+ ))
+ );
+ }
+
+ // OUT
+ private List getDefaultMainViewState() {
+ return Arrays.asList(
+ new MainViewState(0, "2021-05-12", "Reunion A - 15:10 - Mario", "Mario", R.color.mario, "stephane@monmail.fr - peteretsteven@monmail.fr"
+ ),
+ new MainViewState(1, "2021-11-02", "Reunion B - 09:00 - Peach", "Peach", R.color.peach, "warin@monmail.fr - peteretsteven@monmail.fr"
+ ),
+ new MainViewState(2, "2021-06-15", "Reunion C - 11:08 - Goomba", "Goomba", R.color.goomba, "philibert@monmail.fr - peteretsteven@monmail.fr"
+ ),
+ new MainViewState(3, "2021-10-02", "Reunion D - 16:50 - Boo", "Boo", R.color.boo, "krabulbe@monmail.fr - peteretsteven@monmail.fr"
+ )
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/kardabel/mareu/ui/list/utils/LiveDataTestUtils.java b/app/src/test/java/com/kardabel/mareu/ui/list/utils/LiveDataTestUtils.java
new file mode 100644
index 0000000..496191e
--- /dev/null
+++ b/app/src/test/java/com/kardabel/mareu/ui/list/utils/LiveDataTestUtils.java
@@ -0,0 +1,30 @@
+package com.kardabel.mareu.ui.list.utils;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class LiveDataTestUtils {
+ public static T getOrAwaitValue(final LiveData liveData) throws InterruptedException {
+ final Object[] data = new Object[1];
+ final CountDownLatch latch = new CountDownLatch(1);
+ Observer observer = new Observer() {
+ @Override
+ public void onChanged(@Nullable T o) {
+ data[0] = o;
+ latch.countDown();
+ liveData.removeObserver(this);
+ }
+ };
+ liveData.observeForever(observer);
+ // Don't wait indefinitely if the LiveData is not set.
+ if (!latch.await(2, TimeUnit.SECONDS)) {
+ throw new RuntimeException("LiveData value was never set.");
+ }
+ //noinspection unchecked
+ return (T) data[0];
+ }
+}
\ No newline at end of file
diff --git a/avatars/boo.png b/avatars/boo.png
new file mode 100644
index 0000000..7a20b24
Binary files /dev/null and b/avatars/boo.png differ
diff --git a/avatars/donkey.png b/avatars/donkey.png
new file mode 100644
index 0000000..3d0d181
Binary files /dev/null and b/avatars/donkey.png differ
diff --git a/avatars/goomba.png b/avatars/goomba.png
new file mode 100644
index 0000000..6cb9dee
Binary files /dev/null and b/avatars/goomba.png differ
diff --git a/avatars/kamek.png b/avatars/kamek.png
new file mode 100644
index 0000000..36c349f
Binary files /dev/null and b/avatars/kamek.png differ
diff --git a/avatars/koopa.png b/avatars/koopa.png
new file mode 100644
index 0000000..43265fb
Binary files /dev/null and b/avatars/koopa.png differ
diff --git a/avatars/luigi.png b/avatars/luigi.png
new file mode 100644
index 0000000..8d4325c
Binary files /dev/null and b/avatars/luigi.png differ
diff --git a/avatars/mario.png b/avatars/mario.png
new file mode 100644
index 0000000..2db2838
Binary files /dev/null and b/avatars/mario.png differ
diff --git a/avatars/peach.png b/avatars/peach.png
new file mode 100644
index 0000000..6f918fb
Binary files /dev/null and b/avatars/peach.png differ
diff --git a/avatars/toad.png b/avatars/toad.png
new file mode 100644
index 0000000..298090f
Binary files /dev/null and b/avatars/toad.png differ
diff --git a/avatars/yoshi.png b/avatars/yoshi.png
new file mode 100644
index 0000000..4752fa2
Binary files /dev/null and b/avatars/yoshi.png differ
diff --git a/instrumentedTests.zip b/instrumentedTests.zip
new file mode 100644
index 0000000..efa3ed1
Binary files /dev/null and b/instrumentedTests.zip differ
diff --git a/unitTests.zip b/unitTests.zip
new file mode 100644
index 0000000..26a6eed
Binary files /dev/null and b/unitTests.zip differ