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 + + + + + + + + + +
+ +
+ +
+
+ + + 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 + + + + + + + + + +
+ +
+ +
+
+ + + 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... + + + + + + + + + +
+ +
+ +
+
+ + + 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"> + + + + + + + + + + + +