From cc2515d27d78a5c7a32fa24f39237b89249fd83a Mon Sep 17 00:00:00 2001 From: Mariem Baccari <53703829+MariemBaccari@users.noreply.github.com> Date: Sat, 20 May 2023 09:14:29 +0200 Subject: [PATCH] Add my reviews (#196) * Add my reviews * fix fragment title * fix test * fix tests --- .idea/androidTestResultsUserPreferences.xml | 13 ++ .../github/sdp/mediato/FeedFragmentTest.java | 1 + .../sdp/mediato/MyReviewsFragmentTest.java | 170 ++++++++++++++++++ .../com/github/sdp/mediato/MainActivity.java | 19 +- .../github/sdp/mediato/ui/FeedFragment.java | 22 ++- .../mediato/ui/viewmodel/FeedViewModel.java | 22 ++- app/src/main/res/drawable/ic_my_reviews.xml | 5 + app/src/main/res/menu/bottom_menu.xml | 4 + 8 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 app/src/androidTest/java/com/github/sdp/mediato/MyReviewsFragmentTest.java create mode 100644 app/src/main/res/drawable/ic_my_reviews.xml diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index a1723b9b..b6d7ae07 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -98,6 +98,19 @@ + + + + + + + diff --git a/app/src/androidTest/java/com/github/sdp/mediato/FeedFragmentTest.java b/app/src/androidTest/java/com/github/sdp/mediato/FeedFragmentTest.java index 6c015b14..5b1263ae 100644 --- a/app/src/androidTest/java/com/github/sdp/mediato/FeedFragmentTest.java +++ b/app/src/androidTest/java/com/github/sdp/mediato/FeedFragmentTest.java @@ -78,6 +78,7 @@ public void setUp() throws ExecutionException, InterruptedException, TimeoutExce // Pass the username to the fragment Bundle bundle = new Bundle(); bundle.putString("username", user1.getUsername()); + bundle.putSerializable("feedType", FeedFragment.FeedType.FEED); feedFragment.setArguments(bundle); fragmentManager.beginTransaction().replace(R.id.main_container, feedFragment) .commitAllowingStateLoss(); diff --git a/app/src/androidTest/java/com/github/sdp/mediato/MyReviewsFragmentTest.java b/app/src/androidTest/java/com/github/sdp/mediato/MyReviewsFragmentTest.java new file mode 100644 index 00000000..7e2a0475 --- /dev/null +++ b/app/src/androidTest/java/com/github/sdp/mediato/MyReviewsFragmentTest.java @@ -0,0 +1,170 @@ +package com.github.sdp.mediato; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +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.adevinta.android.barista.assertion.BaristaRecyclerViewAssertions.assertRecyclerViewItemCount; +import static com.adevinta.android.barista.interaction.BaristaListInteractions.clickListItemChild; +import static com.adevinta.android.barista.internal.matcher.HelperMatchers.atPosition; + +import static org.hamcrest.Matchers.allOf; + +import android.os.Bundle; + +import androidx.fragment.app.FragmentManager; +import androidx.test.core.app.ActivityScenario; +import androidx.test.espresso.ViewInteraction; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.github.sdp.mediato.DatabaseTests.DataBaseTestUtil; +import com.github.sdp.mediato.data.CollectionsDatabase; +import com.github.sdp.mediato.data.UserDatabase; +import com.github.sdp.mediato.model.Location; +import com.github.sdp.mediato.model.Review; +import com.github.sdp.mediato.model.User; +import com.github.sdp.mediato.model.media.Collection; +import com.github.sdp.mediato.model.media.Media; +import com.github.sdp.mediato.model.media.MediaType; +import com.github.sdp.mediato.ui.FeedFragment; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class MyReviewsFragmentTest { + private final static int STANDARD_USER_TIMEOUT = 10; + private User user1; + private User user2; + private User user3; + private Collection collection1; + private Collection collection2; + private Collection collection3; + + ViewInteraction feedText = onView(withId(R.id.text_feed)); + + @Before + public void setUp() throws ExecutionException, InterruptedException, TimeoutException { + try { + DataBaseTestUtil.useEmulator(); + } catch (Exception ignored) { + } + + //Setup test data + createUsers(); + createReviews(); + addReviews(); + Thread.sleep(1000); + UserDatabase.followUser(user1.getUsername(), user2.getUsername()); + Thread.sleep(1000); + + // Launch the MainActivity + ActivityScenario scenario = ActivityScenario.launch(MainActivity.class); + // Set up the MainActivity to display the FeedFragment + scenario.onActivity(activity -> { + FragmentManager fragmentManager = activity.getSupportFragmentManager(); + FeedFragment feedFragment = new FeedFragment(); + + // Pass the username to the fragment + Bundle bundle = new Bundle(); + bundle.putString("username", user1.getUsername()); + bundle.putSerializable("feedType", FeedFragment.FeedType.MY_REVIEWS); + feedFragment.setArguments(bundle); + fragmentManager.beginTransaction().replace(R.id.main_container, feedFragment) + .commitAllowingStateLoss(); + }); + } + + @AfterClass + public static void cleanDatabase() { + DataBaseTestUtil.cleanDatabase(); + } + + // Test whether the feed text is displayed and contains the correct text + @Test + public void testFeedFragmentTextView() { + feedText.check(matches(isDisplayed())); + feedText.check(matches(withText("My Reviews"))); + } + + // Test that all the reviews from the followed users are displayed + // In this test, only user 2 is followed and they have 2 reviews + @Test + public void testItemCount() throws InterruptedException { + Thread.sleep(5000); + assertRecyclerViewItemCount(R.id.feed_posts, 2); + } + + /** + * --------------Util functions-------------------- + */ + + //Helper function that creates users and adds them to the database + private void createUsers() throws ExecutionException, InterruptedException, TimeoutException { + //Create new sample users + user1 = new User.UserBuilder("uniqueId1") + .setUsername("user_myreviews_test_1") + .setEmail("email_test_1") + .setRegisterDate("09/03/2023") + .setLocation(new Location(3.14, 3.14)) + .build(); + user2 = new User.UserBuilder("uniqueId2") + .setUsername("user_myreviews_test_2") + .setEmail("email_test_2") + .setRegisterDate("19/03/2023") + .setLocation(new Location(3.14, 3.14)) + .build(); + user3 = new User.UserBuilder("uniqueId3") + .setUsername("user_myreviews_test_3") + .setEmail("email_test_3") + .setRegisterDate("19/03/2023") + .setLocation(new Location(3.14, 3.14)) + .build(); + UserDatabase.addUser(user1).get(STANDARD_USER_TIMEOUT, TimeUnit.SECONDS); + UserDatabase.addUser(user2).get(STANDARD_USER_TIMEOUT, TimeUnit.SECONDS); + UserDatabase.addUser(user3).get(STANDARD_USER_TIMEOUT, TimeUnit.SECONDS); + + } + + //Helper function that creates reviews and adds them to the collections + private void createReviews() { + Media media1 = new Media(MediaType.MOVIE, "Harry Potter 1", "In the closet", "validUrl", 123); + Media media2 = new Media(MediaType.MOVIE, "Harry Potter 2", "In the WC", "validUrl", 1234); + Media media3 = new Media(MediaType.MOVIE, "Harry Potter 3", "In the prison", "validUrl", 12345); + + Review review1 = new Review(user1.getUsername(), media1, 4, "Best movie in the world"); + Review review2 = new Review(user1.getUsername(), media2, 5, "Pretty bad"); + Review review3 = new Review(user1.getUsername(), media3, 6, "Really bad"); + + Map collection1Reviews = new HashMap<>(); + collection1Reviews.put(review1.getMedia().getTitle(), review1); + + Map collection2Reviews = new HashMap<>(); + collection2Reviews.put(review2.getMedia().getTitle(), review2); + + Map collection3Reviews = new HashMap<>(); + collection3Reviews.put(review3.getMedia().getTitle(), review3); + + collection1 = new Collection("The best", collection1Reviews); + collection2 = new Collection("The bad", collection2Reviews); + collection3 = new Collection("The worst", collection3Reviews); + } + + //Helper function that adds the reviews to the database + private void addReviews() throws ExecutionException, InterruptedException, TimeoutException { + CollectionsDatabase.addCollection(user1.getUsername(), collection1); + CollectionsDatabase.addCollection(user1.getUsername(), collection2); + CollectionsDatabase.addCollection(user3.getUsername(), collection3); + Thread.sleep(1000); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/sdp/mediato/MainActivity.java b/app/src/main/java/com/github/sdp/mediato/MainActivity.java index 3b7b5ce4..a5c21359 100644 --- a/app/src/main/java/com/github/sdp/mediato/MainActivity.java +++ b/app/src/main/java/com/github/sdp/mediato/MainActivity.java @@ -15,7 +15,6 @@ import com.github.sdp.mediato.cache.AppCache; import com.github.sdp.mediato.databinding.ActivityMainBinding; import com.github.sdp.mediato.ui.ExploreFragment; -import com.github.sdp.mediato.model.Review; import com.github.sdp.mediato.ui.FeedFragment; import com.github.sdp.mediato.ui.MyProfileFragment; import com.github.sdp.mediato.ui.ReadOnlyProfileFragment; @@ -23,7 +22,6 @@ import com.github.sdp.mediato.ui.viewmodel.MyProfileViewModel; import com.github.sdp.mediato.ui.viewmodel.ReadOnlyProfileViewModel; import com.google.firebase.auth.FirebaseAuth; -import com.google.gson.Gson; /** * The main activity of the app that displays a bottom navigation bar and manages the navigation @@ -37,6 +35,7 @@ public class MainActivity extends AppCompatActivity implements FragmentSwitcher SearchFragment searchFragment; ExploreFragment exploreFragment; FeedFragment feedFragment; + FeedFragment myReviewsFragment; private MyProfileViewModel myProfileViewModel; private ReadOnlyProfileViewModel readOnlyProfileViewModel; AppCache globalCache; @@ -78,8 +77,9 @@ private boolean navigateFragments(int itemId) { replaceFragment(myProfileFragment); } else if (itemId == R.id.explore) { replaceFragment(exploreFragment); + } else if (itemId == R.id.my_reviews) { + replaceFragment(myReviewsFragment); } - return true; } @@ -102,18 +102,29 @@ private void setDefaultFragment(String username) { searchFragment = new SearchFragment(); exploreFragment = new ExploreFragment(); feedFragment = new FeedFragment(); + myReviewsFragment = new FeedFragment(); Bundle args = new Bundle(); + Bundle argsFeed = new Bundle(); + Bundle argsMyReviews = new Bundle(); + // Give the username as an argument to the profile page and switch to it args.putString("username", username); args.putString("general_search", "true"); args.putString("collection", "Recently watched"); + argsFeed.putString("username", username); + argsMyReviews.putString("username", username); + + argsFeed.putSerializable("feedType", FeedFragment.FeedType.FEED); + argsMyReviews.putSerializable("feedType", FeedFragment.FeedType.MY_REVIEWS); + searchFragment.setArguments(args); myProfileFragment.setArguments(args); exploreFragment.setArguments(args); - feedFragment.setArguments(args); + feedFragment.setArguments(argsFeed); + myReviewsFragment.setArguments(argsMyReviews); // Mark the profile item in the bottom bar as selected binding.bottomNavigationView.setSelectedItemId(R.id.profile); diff --git a/app/src/main/java/com/github/sdp/mediato/ui/FeedFragment.java b/app/src/main/java/com/github/sdp/mediato/ui/FeedFragment.java index 34fc9c6e..9db95050 100644 --- a/app/src/main/java/com/github/sdp/mediato/ui/FeedFragment.java +++ b/app/src/main/java/com/github/sdp/mediato/ui/FeedFragment.java @@ -25,6 +25,7 @@ public class FeedFragment extends Fragment { private FeedViewModel viewModel; private FragmentFeedBinding binding; private ReviewPostListAdapter adapter; + private FeedType feedType; @Override @@ -38,9 +39,12 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); USERNAME = getArguments().getString("username"); + feedType = (FeedType) getArguments().getSerializable("feedType"); + + binding.textFeed.setText(feedType.toString()); viewModel = new ViewModelProvider(this).get(FeedViewModel.class); - viewModel.setUsername(USERNAME); + viewModel.setData(USERNAME, feedType); adapter = new ReviewPostListAdapter(); adapter.setUsername(USERNAME); @@ -51,4 +55,20 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat binding.feedPosts.setHasFixedSize(false); viewModel.getPosts().observe(getViewLifecycleOwner(), adapter::submitList); } + + public enum FeedType { + MY_REVIEWS, FEED; + + @Override + public String toString() { + switch (this) { + case MY_REVIEWS: + return "My Reviews"; + case FEED: + return "Feed"; + } + return ""; + } + } + } diff --git a/app/src/main/java/com/github/sdp/mediato/ui/viewmodel/FeedViewModel.java b/app/src/main/java/com/github/sdp/mediato/ui/viewmodel/FeedViewModel.java index bfa6fc1f..57764039 100644 --- a/app/src/main/java/com/github/sdp/mediato/ui/viewmodel/FeedViewModel.java +++ b/app/src/main/java/com/github/sdp/mediato/ui/viewmodel/FeedViewModel.java @@ -11,6 +11,7 @@ import com.github.sdp.mediato.data.UserDatabase; import com.github.sdp.mediato.model.User; import com.github.sdp.mediato.model.post.ReviewPost; +import com.github.sdp.mediato.ui.FeedFragment; import java.util.ArrayList; import java.util.List; @@ -21,7 +22,6 @@ public class FeedViewModel extends AndroidViewModel { Application application; private final MutableLiveData> posts = new MutableLiveData<>(new ArrayList<>()); private String username; - public FeedViewModel (@NonNull Application application) { super(application); this.application = application; @@ -31,9 +31,16 @@ public FeedViewModel (@NonNull Application application) { * Sets the username of the user who is currently logged in and generates the list of posts * @param username the username of the user who is currently logged in */ - public void setUsername(String username) { + public void setData(String username, FeedFragment.FeedType feedType) { this.username = username; - createFollowingsPosts(); + switch (feedType) { + case MY_REVIEWS: + createMyPosts(); + break; + case FEED: + createFollowingsPosts(); + break; + } } /** @@ -59,4 +66,13 @@ public void createFollowingsPosts() { .thenAccept(posts::setValue); } + /** + * Generates the list of posts from the current user + */ + public void createMyPosts() { + UserDatabase.getUser(username) + .thenApply(user -> user.fetchReviewPosts()) + .thenAccept(posts::setValue); + } + } diff --git a/app/src/main/res/drawable/ic_my_reviews.xml b/app/src/main/res/drawable/ic_my_reviews.xml new file mode 100644 index 00000000..ff022725 --- /dev/null +++ b/app/src/main/res/drawable/ic_my_reviews.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/menu/bottom_menu.xml b/app/src/main/res/menu/bottom_menu.xml index 59b5ac34..eab06c61 100644 --- a/app/src/main/res/menu/bottom_menu.xml +++ b/app/src/main/res/menu/bottom_menu.xml @@ -12,6 +12,10 @@ android:id="@+id/search" android:title="Search" android:icon="@drawable/ic_search"/> +