Repository: cmput301-w25/project-kernelcrew
Pull request #231: Edit, Delete button shown according to Mood ownership
Author: login: adityadipakpatel
Created: 2025-03-20T01:55:58Z | Merged: 2025-03-26T01:38:51Z

Resolves: #195 
Resolves: #112
Resolves: #201 

## Summary

 - User can only see, click the edit mood, delete mood button(s) only if they own the mood
 -  Removed the "view profile button"
 - Added a username field which displays the mood owner on mood details page
     - and it redirects to other user profile page upon click

## Testing

- How should a reviewer test this feature, fix, or UI?
  - Edit and Delete Permissions
     - Click on "view details" button on various moods(yours, not yours)
     - verify if the edit, delete mood buttons are only displayed on the moods you own
  - View user details
     - Go to the Mood Details page, check if the "username" of the mood owner is displayed?
     - Check clicking that button to verify if it redirects you to the "other user profile page"
         - Verify if the details on the "other user profile page" are correct(the mood owner), and same as on the mood details page
- Are there any specific commands, steps, or credentials needed?
     - N/A

## Merge Instructions

- Any considerations that other PR's may need to take into note? 
     - N/A
- Should the reviewer delete the branch on merge?
     - Yes

---

**Checklist**
- [x] Are all relevant issues referenced by this PR?
- [x] Does the app still build (locally)?
- [x] Do all tests pass?
- [x] Does the feature work as expected?
- [x] Does this feature preserve the app’s existing stability?  
- [x] Is the description of the changes correct/present?
- [x] Have new tests been created or existing tests updated as needed?

---


Diff:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index d3f5570dc1..df51fb7799 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,18 +1,22 @@
 ## Summary
 
-Give a brief summery of what has been implemented
+A brief summary of features implemented
 
 Resolves: #...
 
 ## Testing
 
 - How should a reviewer test this feature, fix, or UI?
+     - 
 - Are there any specific commands, steps, or credentials needed?
+     - 
 
 ## Merge Instructions
 
 - Any considerations that other PR's may need to take into note? 
-- Should the reviewer delete the branch on merge? Y/N
+     - 
+- Should the reviewer delete the branch on merge? 
+     - Yes/No
 
 ---
 
diff --git a/code/app/src/androidTest/java/com/kernelcrew/moodapp/LocationInstrumentedTest.java b/code/app/src/androidTest/java/com/kernelcrew/moodapp/LocationInstrumentedTest.java
index 16ce15eacd..0b12817c49 100644
--- a/code/app/src/androidTest/java/com/kernelcrew/moodapp/LocationInstrumentedTest.java
+++ b/code/app/src/androidTest/java/com/kernelcrew/moodapp/LocationInstrumentedTest.java
@@ -1,17 +1,15 @@
 package com.kernelcrew.moodapp;
 
 import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.Espresso.pressBack;
 import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
 import static androidx.test.espresso.action.ViewActions.replaceText;
 import static androidx.test.espresso.action.ViewActions.scrollTo;
-import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
-
 import static org.awaitility.Awaitility.await;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
 
 import android.os.SystemClock;
 
@@ -124,10 +122,8 @@ public void testSubmitFormWithoutLocation() {
         // Select an emotion
         onView(withId(R.id.toggle_happy)).perform(click());
 
-        // Scroll to the trigger field first to ensure it's visible
         onView(withId(R.id.emotion_reason)).perform(scrollTo());
 
-        // Add some text to the trigger field - separate actions for stability
         onView(withId(R.id.emotion_reason)).perform(replaceText("Location Test"));
         onView(withId(R.id.emotion_reason)).perform(closeSoftKeyboard());
 
diff --git a/code/app/src/androidTest/java/com/kernelcrew/moodapp/MoodOwnershipTest.java b/code/app/src/androidTest/java/com/kernelcrew/moodapp/MoodOwnershipTest.java
new file mode 100644
index 0000000000..5078dc61fd
--- /dev/null
+++ b/code/app/src/androidTest/java/com/kernelcrew/moodapp/MoodOwnershipTest.java
@@ -0,0 +1,200 @@
+package com.kernelcrew.moodapp;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
+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.contrib.RecyclerViewActions.actionOnItemAtPosition;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static com.kernelcrew.moodapp.MoodDetailsNavigationTest.clickChildViewWithId;
+import static org.hamcrest.Matchers.not;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import com.google.android.gms.tasks.Tasks;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
+import com.google.firebase.firestore.DocumentSnapshot;
+import com.google.firebase.firestore.FirebaseFirestore;
+import com.kernelcrew.moodapp.ui.MainActivity;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class MoodOwnershipTest extends FirebaseEmulatorMixin {
+
+    // User 1 credentials
+    private static final String USER1_USERNAME = "automatedtests1";
+    private static final String USER1_EMAIL = "automatedtests1@kernelcrew.com";
+    private static final String USER1_PASSWORD = "AT1@1234";
+
+    // User 2 credentials
+    private static final String USER2_USERNAME = "automatedtests2";
+    private static final String USER2_EMAIL = "automatedtests2@kernelcrew.com";
+    private static final String USER2_PASSWORD = "AT2@1234";
+
+    // Mood details for test creation
+    private static final String MOOD1_REASON = "Feeling Energized";
+
+    /**
+     * Helper method to delete a test user if it exists.
+     */
+    private static void deleteTestUser(String email, String password, String username) throws Exception {
+        FirebaseAuth auth = FirebaseAuth.getInstance();
+        try {
+            Tasks.await(auth.signInWithEmailAndPassword(email, password));
+            FirebaseUser user = auth.getCurrentUser();
+            if (user != null) {
+                Tasks.await(user.delete());
+            }
+        } catch (Exception e) {
+            // User likely doesn't exist or sign-in failed
+        } finally {
+            auth.signOut();
+        }
+        FirebaseFirestore db = FirebaseFirestore.getInstance();
+        try {
+            // Delete the username doc
+            Tasks.await(db.collection("usernames").document(username).delete());
+        } catch (Exception ignored) { }
+        try {
+            // Delete the user doc
+            List<DocumentSnapshot> docs = Tasks.await(
+                    db.collection("users").whereEqualTo("email", email).get()
+            ).getDocuments();
+            for (DocumentSnapshot doc : docs) {
+                Tasks.await(db.collection("users").document(doc.getId()).delete());
+            }
+        } catch (Exception ignored) { }
+    }
+
+    @BeforeClass
+    public static void cleanUpExistingUsers() throws Exception {
+        System.out.println("Deleting test user 1 if exists...");
+        deleteTestUser(USER1_EMAIL, USER1_PASSWORD, USER1_USERNAME);
+        System.out.println("Deleting test user 2 if exists...");
+        deleteTestUser(USER2_EMAIL, USER2_PASSWORD, USER2_USERNAME);
+        SystemClock.sleep(4000);
+    }
+
+    /**
+     * Combined test: 
+     * - Sign up as User1, create a mood via GUI and verify that Edit/Delete buttons are displayed.
+     * - Sign out User1.
+     * - Sign up as User2, view User1’s mood details, and verify that Edit/Delete buttons are not displayed.
+     */
+    @Test
+    public void test_MoodOwnershipVerification() throws InterruptedException, ExecutionException {
+        // --- User1 Flow ---
+        ActivityScenario<MainActivity> scenario1 = ActivityScenario.launch(MainActivity.class);
+        SystemClock.sleep(3000);
+
+        // Sign up as User1
+        onView(withId(R.id.buttonInitialToSignUp)).perform(click());
+        onView(withId(R.id.username)).perform(replaceText(USER1_USERNAME));
+        onView(withId(R.id.emailSignUp)).perform(replaceText(USER1_EMAIL));
+        onView(withId(R.id.passwordSignUp)).perform(replaceText(USER1_PASSWORD));
+        onView(withId(R.id.signUpButtonAuthToHome)).perform(click());
+        SystemClock.sleep(3000);
+
+        // Create a mood as User1 via GUI
+        onView(withId(R.id.page_createMoodEvent)).perform(click());
+        SystemClock.sleep(2000);
+        onView(withId(R.id.emotion_reason))
+                .perform(scrollTo(), replaceText(MOOD1_REASON), closeSoftKeyboard());
+        onView(withId(R.id.toggle_happy)).perform(click());
+        onView(withId(R.id.submit_button)).perform(scrollTo(), click());
+        SystemClock.sleep(3000);
+
+        // Open the newly created mood's details (position 0)
+        onView(withId(R.id.moodRecyclerView))
+                .perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.viewDetailsButton)));
+        SystemClock.sleep(3000);
+
+        // Verify that Edit/Delete buttons are displayed for User1's own mood
+        onView(withId(R.id.btnEditMood)).check(matches(isDisplayed()));
+        onView(withId(R.id.btnDeleteMood)).check(matches(isDisplayed()));
+        SystemClock.sleep(2000);
+
+        // Sign out User1
+        FirebaseAuth.getInstance().signOut();
+        SystemClock.sleep(3000);
+        scenario1.close();
+
+        // --- User2 Flow ---
+        ActivityScenario<MainActivity> scenario2 = ActivityScenario.launch(MainActivity.class);
+        SystemClock.sleep(3000);
+
+        // Sign up as User2
+        onView(withId(R.id.buttonInitialToSignUp)).perform(click());
+        onView(withId(R.id.username)).perform(replaceText(USER2_USERNAME));
+        onView(withId(R.id.emailSignUp)).perform(replaceText(USER2_EMAIL));
+        onView(withId(R.id.passwordSignUp)).perform(replaceText(USER2_PASSWORD));
+        onView(withId(R.id.signUpButtonAuthToHome)).perform(click());
+        SystemClock.sleep(3000);
+
+        // Open the mood details of the mood created by User1 (should appear at position 0)
+        onView(withId(R.id.moodRecyclerView))
+                .perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.viewDetailsButton)));
+        SystemClock.sleep(3000);
+
+        // Verify that Edit/Delete buttons are NOT displayed for a mood not owned by User2
+        onView(withId(R.id.btnEditMood)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.btnDeleteMood)).check(matches(not(isDisplayed())));
+        SystemClock.sleep(3000);
+
+        // Sign out User2
+        FirebaseAuth.getInstance().signOut();
+        SystemClock.sleep(3000);
+        scenario2.close();
+    }
+
+    // code from lab 7
+    @AfterClass
+    public static void tearDown() {
+        String projectId = "kernel-crew-mood-app";
+        URL url = null;
+        try {
+            url = new URL("http://10.0.2.2:4000/emulator/v1/projects/" + projectId + "/databases/(default)/documents");
+        } catch (MalformedURLException exception) {
+            Log.e("URL Error", Objects.requireNonNull(exception.getMessage()));
+        }
+        HttpURLConnection urlConnection = null;
+        try {
+            assert url != null;
+            urlConnection = (HttpURLConnection) url.openConnection();
+            urlConnection.setRequestMethod("DELETE");
+            int response = urlConnection.getResponseCode();
+            Log.i("Response Code", "Response Code: " + response);
+        } catch (IOException exception) {
+            Log.e("IO Error", Objects.requireNonNull(exception.getMessage()));
+        } finally {
+            if (urlConnection != null) {
+                urlConnection.disconnect();
+            }
+        }
+    }
+}
diff --git a/code/app/src/androidTest/java/com/kernelcrew/moodapp/OtherProfilePageNavigationTest.java b/code/app/src/androidTest/java/com/kernelcrew/moodapp/OtherProfilePageNavigationTest.java
index 0f00cfe1cf..a1234b35d5 100644
--- a/code/app/src/androidTest/java/com/kernelcrew/moodapp/OtherProfilePageNavigationTest.java
+++ b/code/app/src/androidTest/java/com/kernelcrew/moodapp/OtherProfilePageNavigationTest.java
@@ -6,15 +6,15 @@
 import static androidx.test.espresso.action.ViewActions.replaceText;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
-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.kernelcrew.moodapp.MoodDetailsNavigationTest.clickChildViewWithId;
-import static com.kernelcrew.moodapp.MoodDetailsNavigationTest.scrollNestedScrollViewToBottom;
 
 import android.os.SystemClock;
+
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+
 import com.google.android.gms.tasks.Tasks;
 import com.google.firebase.auth.FirebaseAuth;
 import com.google.firebase.firestore.FirebaseFirestore;
@@ -23,6 +23,7 @@
 import com.kernelcrew.moodapp.data.MoodEvent;
 import com.kernelcrew.moodapp.data.MoodEventProvider;
 import com.kernelcrew.moodapp.ui.MainActivity;
+
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
@@ -30,6 +31,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
@@ -121,7 +123,7 @@ public void setupAuth() throws InterruptedException, ExecutionException {
     @Test
     public void testViewOtherProfilePageNavigationFromMoodDetails() throws InterruptedException, ExecutionException {
         onView(withText("Sign In")).perform(click());
-        SystemClock.sleep(3000);
+        SystemClock.sleep(500);
 
         onView(withId(R.id.emailSignIn))
                 .perform(replaceText(USER_EMAIL), closeSoftKeyboard());
@@ -131,25 +133,18 @@ public void testViewOtherProfilePageNavigationFromMoodDetails() throws Interrupt
                 .perform(click());
 
         // Wait for HomeFeed to load data.
-        SystemClock.sleep(3000);
+        SystemClock.sleep(500);
 
         // Click on the first mood item's "View Details" button.
         onView(withId(R.id.moodRecyclerView))
                 .perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.viewDetailsButton)));
 
-        // Wait for the MoodDetails screen to load.
-        SystemClock.sleep(3000);
-
-        // Scroll the NestedScrollView to the bottom.
-        onView(withId(R.id.nestedScrollView))
-                .perform(scrollNestedScrollViewToBottom());
-
         // Click the "View Profile" button.
-        onView(withId(R.id.btnViewProfile))
+        onView(withId(R.id.tvUsernameDisplay))
                 .perform(click());
 
         // Wait for the OtherUserProfile screen to load.
-        SystemClock.sleep(3000);
+        SystemClock.sleep(500);
 
         // Verify that the OtherUserProfile screen displays the expected username (contains "testUser")
         // and that the email matches.
diff --git a/code/app/src/main/java/com/kernelcrew/moodapp/data/UserProvider.java b/code/app/src/main/java/com/kernelcrew/moodapp/data/UserProvider.java
index 2f7dfc5783..d9ac37bc73 100644
--- a/code/app/src/main/java/com/kernelcrew/moodapp/data/UserProvider.java
+++ b/code/app/src/main/java/com/kernelcrew/moodapp/data/UserProvider.java
@@ -14,7 +14,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 public class UserProvider {
     private final FirebaseFirestore db;
 
@@ -82,7 +81,7 @@ public Task<List<User>> fetchFollowers(@NonNull String uid) {
     /**
      * Fetch all users followed by a specific user.
      * @param uid The user to look up the following of.
-     * @return All users followed bt this user.
+     * @return All users followed by this user.
      */
     public Task<List<User>> fetchFollowing(@NonNull String uid) {
         Task<QuerySnapshot> query = db.collection("users").document(uid).collection("following").get();
@@ -111,4 +110,33 @@ public Task<List<User>> fetchFollowing(@NonNull String uid) {
     public void addSnapshotListenerForUser(@NonNull String uid, @NonNull EventListener<DocumentSnapshot> listener) {
         db.collection("users").document(uid).addSnapshotListener(listener);
     }
-}
\ No newline at end of file
+
+    /**
+     * Fetch the username for a specific user.
+     * @param uid The user ID whose username to fetch.
+     * @return A Task containing the username as a String.
+     */
+    public Task<String> fetchUsername(@NonNull String uid) {
+        return db.collection("users")
+                .document(uid)
+                .get()
+                .continueWith(task -> {
+                    if (!task.isSuccessful()) {
+                        throw task.getException();
+                    }
+                    DocumentSnapshot document = task.getResult();
+                    if (document != null && document.exists()) {
+                        String username = document.getString("username");
+                        if (username == null) {
+                            username = document.getString("name");
+                        }
+                        if (username == null) {
+                            username = "UnknownUser";
+                        }
+                        return username;
+                    } else {
+                        throw new Exception("User not found");
+                    }
+                });
+    }
+}
diff --git a/code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java b/code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
index ee496941b5..1aad075734 100644
--- a/code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
+++ b/code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
@@ -7,9 +7,9 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.Button;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -17,21 +17,23 @@
 import androidx.fragment.app.Fragment;
 import androidx.navigation.fragment.NavHostFragment;
 
-import com.bumptech.glide.Glide;
 import com.google.android.material.appbar.MaterialToolbar;
+import com.google.android.material.chip.Chip;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseUser;
 import com.google.firebase.firestore.FirebaseFirestore;
 import com.kernelcrew.moodapp.R;
 import com.kernelcrew.moodapp.data.MoodEvent;
 import com.kernelcrew.moodapp.data.MoodEventProvider;
+import com.kernelcrew.moodapp.data.UserProvider;
 
 public class MoodDetails extends Fragment implements DeleteDialogFragment.DeleteDialogListener {
 
     private MaterialToolbar toolbar;
     private ImageView imageMoodIcon, ivMoodPhoto;
-    private TextView tvMoodState, tvTriggerValue, tvSocialSituationValue, tvReasonValue;
+    private TextView tvMoodState, tvSocialSituationValue, tvReasonValue;
+    private Chip tvUsernameDisplay;
     private Button btnEditMood;
-    private Button btnViewProfile;
-
     private Button btnDeleteMood;
 
     private Button btnMoodComments;
@@ -41,13 +43,12 @@ public class MoodDetails extends Fragment implements DeleteDialogFragment.Delete
 
     // Document ID for the mood event and source of navigation
     private String moodEventId;
-    private String sourceScreen; // e.g., "home" or "filtered"
+    private String sourceScreen;
 
-    // Use UID to identify the user (instead of a username)
+    // Use UID to identify the user
     private String userId;
 
     public MoodDetails() {
-        // Required empty public constructor
     }
 
     @Override
@@ -72,6 +73,7 @@ public View onCreateView(@NonNull LayoutInflater inflater,
                              @Nullable Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_mood_details, container, false);
 
+        tvUsernameDisplay = view.findViewById(R.id.tvUsernameDisplay);
         toolbar = view.findViewById(R.id.moodDetailsToolbar);
         imageMoodIcon = view.findViewById(R.id.imageMoodIcon);
         tvMoodState = view.findViewById(R.id.tvMoodState);
@@ -80,25 +82,10 @@ public View onCreateView(@NonNull LayoutInflater inflater,
         ivMoodPhoto = view.findViewById(R.id.ivMoodPhoto);
         btnEditMood = view.findViewById(R.id.btnEditMood);
         btnDeleteMood = view.findViewById(R.id.btnDeleteMood);
-        btnViewProfile = view.findViewById(R.id.btnViewProfile);
         btnMoodComments = view.findViewById(R.id.btnMoodComments);
 
         toolbar.setNavigationOnClickListener(v -> handleBackButton());
 
-        // Set up "View Profile" button click to navigate to MyProfile, passing UID
-        btnViewProfile.setOnClickListener(v -> {
-            if (userId != null && !userId.isEmpty()) {
-                Bundle args = new Bundle();
-                args.putString("uid", userId);
-                // Ensure your nav_graph.xml includes a destination for OtherUserProfile with the ID otherUserProfile
-                NavHostFragment.findNavController(this)
-                        .navigate(R.id.otherUserProfile, args);
-            } else {
-                Toast.makeText(requireContext(), "User information unavailable.", Toast.LENGTH_SHORT).show();
-            }
-        });
-
-        // Fetch mood details from Firestore
         fetchMoodDetails(moodEventId);
 
         btnEditMood.setOnClickListener(v -> {
@@ -106,13 +93,13 @@ public View onCreateView(@NonNull LayoutInflater inflater,
             args.putString("moodEventId", moodEventId);
             NavHostFragment.findNavController(this).navigate(R.id.editMoodEvent, args);
         });
-        //Logic to delete a mood
+
         btnDeleteMood.setOnClickListener(v -> {
             Bundle args = new Bundle();
             args.putString("moodEventId", moodEventId);
             DeleteDialogFragment dialogFragment = new DeleteDialogFragment();
             dialogFragment.setArguments(args);
-            dialogFragment.setDeleteDialogListener(this); // Add this line
+            dialogFragment.setDeleteDialogListener(this);
             dialogFragment.show(getParentFragmentManager(), "delete_dialog");
         });
 
@@ -123,14 +110,11 @@ public View onCreateView(@NonNull LayoutInflater inflater,
         });
 
         return view;
-
     }
 
-    /**
-     * Fetches the mood details document from Firestore using the moodEventId.
-     */
     private void fetchMoodDetails(String moodEventId) {
         if (moodEventId == null) return;
+
         provider.getMoodEvent(moodEventId)
                 .addOnSuccessListener(moodEvent -> {
                     if (moodEvent != null) {
@@ -152,7 +136,6 @@ private void bindMoodData(MoodEvent moodEvent) {
         tvSocialSituationValue.setText(moodEvent.getSocialSituation());
         tvReasonValue.setText(moodEvent.getReason());
 
-        // Retrieve the UID from the mood event
         userId = moodEvent.getUid();
 
         int moodImageRes = getMoodIconResource(moodEvent.getEmotion().toString());
@@ -162,12 +145,57 @@ private void bindMoodData(MoodEvent moodEvent) {
         if (photo != null) {
             ivMoodPhoto.setImageBitmap(photo);
         }
-    }
 
+        // Fetch username via UserProvider
+        if (userId != null && !userId.isEmpty()) {
+            UserProvider.getInstance().fetchUsername(userId)
+                    .addOnSuccessListener(username -> {
+                        tvUsernameDisplay.setText("@" + username);
+                        // Navigate to OtherUserProfile on click
+                        tvUsernameDisplay.setOnClickListener(v -> {
+                            Bundle args = new Bundle();
+                            args.putString("uid", userId);
+                            NavHostFragment.findNavController(MoodDetails.this)
+                                    .navigate(R.id.otherUserProfile, args);
+                        });
+                    })
+                    .addOnFailureListener(e -> {
+                        tvUsernameDisplay.setText(R.string.error_loading_user);
+                    });
+        } else {
+            tvUsernameDisplay.setText(R.string.no_user_id_provided);
+        }
+
+        // Update edit/delete button visibility based on ownership
+        handleOwnershipUI(userId);
+    }
 
     /**
-     * Handles the back button behavior.
+     * Checks if the current user owns the mood, and updates the edit/delete button visibility.
      */
+    private void handleOwnershipUI(String moodOwnerUid) {
+        FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
+
+        // If no one is logged in or we have no owner, hide everything
+        if (currentUser == null || moodOwnerUid == null) {
+            btnEditMood.setVisibility(View.GONE);
+            btnDeleteMood.setVisibility(View.GONE);
+            return;
+        }
+
+        // Compare the mood's owner ID to the current user's ID
+        String currentUserUid = currentUser.getUid();
+        if (moodOwnerUid.equals(currentUserUid)) {
+            // The current user owns this mood => show the edit/delete buttons
+            btnEditMood.setVisibility(View.VISIBLE);
+            btnDeleteMood.setVisibility(View.VISIBLE);
+        } else {
+            // The current user does NOT own it => hide the buttons
+            btnEditMood.setVisibility(View.GONE);
+            btnDeleteMood.setVisibility(View.GONE);
+        }
+    }
+
     private void handleBackButton() {
         NavHostFragment.findNavController(this).popBackStack();
     }
@@ -175,7 +203,6 @@ private void handleBackButton() {
     @Override
     public void onDeleteConfirmed() {
         Toast.makeText(requireContext(), "Mood deleted successfully", Toast.LENGTH_SHORT).show();
-        // Navigate back to home screen
         NavHostFragment.findNavController(this).popBackStack();
     }
 }
diff --git a/code/app/src/main/res/layout/fragment_mood_details.xml b/code/app/src/main/res/layout/fragment_mood_details.xml
index 74c0fdcbe4..c37e2025cc 100644
--- a/code/app/src/main/res/layout/fragment_mood_details.xml
+++ b/code/app/src/main/res/layout/fragment_mood_details.xml
@@ -54,41 +54,60 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_weight="1"
-                    android:text="Happy"
+                    android:text="@string/mood_type_text"
                     android:textAppearance="?attr/textAppearanceHeadline6"
                     android:textColor="@android:color/black" />
 
                 <com.google.android.material.button.MaterialButton
                     android:id="@+id/btnEditMood"
+                    android:visibility="gone"
                     style="@style/Widget.MaterialComponents.Button"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_marginStart="16dp"
                     android:layout_weight="1"
                     android:backgroundTint="@color/button_purple"
-                    android:text="Edit"
+                    android:text="@string/edit_button_text"
                     android:textColor="@android:color/white"
                     app:cornerRadius="999dp" />
 
                 <com.google.android.material.button.MaterialButton
                     android:id="@+id/btnDeleteMood"
+                    android:visibility="gone"
                     style="@style/Widget.MaterialComponents.Button"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_marginStart="16dp"
                     android:backgroundTint="@color/button_purple"
-                    android:text="Delete"
+                    android:text="@string/delete_button_text"
                     android:textColor="@android:color/white"
                     app:cornerRadius="999dp" />
 
             </LinearLayout>
 
-            <!-- Divider -->
+            <com.google.android.material.chip.Chip
+                android:id="@+id/tvUsernameDisplay"
+                style="@style/Widget.MaterialComponents.Chip.Action"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
 
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="horizontal"></LinearLayout>
+                android:clickable="true"
+                android:focusable="true"
+                android:text="@string/dummy_username"
+                android:textStyle="bold"
+                android:textColor="@android:color/white"
+                android:paddingStart="8dp"
+                android:paddingEnd="16dp"
+
+                app:chipBackgroundColor="@color/button_purple"
+                app:chipCornerRadius="16dp"
+                app:chipIcon="@drawable/ic_person"
+                app:chipIconTint="@android:color/white"
+                app:chipIconVisible="true"
+                app:closeIconEnabled="false"
+                app:iconStartPadding="8dp"
+                app:iconEndPadding="8dp" />
 
             <View
                 android:layout_width="match_parent"
@@ -117,7 +136,7 @@
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
                         android:layout_marginTop="16dp"
-                        android:hint="Social Situation"
+                        android:hint="@string/social_situation_heading"
                         app:boxCornerRadiusTopStart="12dp"
                         app:boxCornerRadiusTopEnd="12dp"
                         app:boxCornerRadiusBottomStart="12dp"
@@ -130,7 +149,7 @@
                             android:id="@+id/tvSocialSituationValue"
                             android:layout_width="match_parent"
                             android:layout_height="wrap_content"
-                            android:text="Input"
+                            android:text="@string/social_situation_box_text"
                             android:enabled="false" />
                     </com.google.android.material.textfield.TextInputLayout>
 
@@ -141,7 +160,7 @@
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
                         android:layout_marginTop="16dp"
-                        android:hint="Reason"
+                        android:hint="@string/reason_heading"
                         app:boxCornerRadiusTopStart="12dp"
                         app:boxCornerRadiusTopEnd="12dp"
                         app:boxCornerRadiusBottomStart="12dp"
@@ -154,7 +173,7 @@
                             android:id="@+id/tvReasonValue"
                             android:layout_width="match_parent"
                             android:layout_height="wrap_content"
-                            android:text="Some Reason"
+                            android:text="@string/reason_box_text"
                             android:enabled="false" />
                     </com.google.android.material.textfield.TextInputLayout>
 
@@ -164,7 +183,7 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_marginTop="24dp"
-                        android:text="Photo"
+                        android:text="@string/photo_heading"
                         android:textAppearance="?attr/textAppearanceSubtitle1"
                         android:textStyle="bold" />
 
@@ -191,7 +210,7 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_marginTop="24dp"
-                        android:text="Location"
+                        android:text="@string/location_heading"
                         android:textAppearance="?attr/textAppearanceSubtitle1"
                         android:textStyle="bold" />
 
@@ -232,17 +251,6 @@
                         android:backgroundTint="@color/button_purple"
                         app:cornerRadius="12dp" />
 
-                    <com.google.android.material.button.MaterialButton
-                        android:id="@+id/btnViewProfile"
-                        style="@style/Widget.MaterialComponents.Button"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginTop="24dp"
-                        android:text="View Profile"
-                        android:textColor="@android:color/white"
-                        android:backgroundTint="@color/button_purple"
-                        app:cornerRadius="12dp" />
-
                 </LinearLayout>
             </com.google.android.material.card.MaterialCardView>
 
diff --git a/code/app/src/main/res/values/strings.xml b/code/app/src/main/res/values/strings.xml
index dffa210cd3..81ce26d398 100644
--- a/code/app/src/main/res/values/strings.xml
+++ b/code/app/src/main/res/values/strings.xml
@@ -41,6 +41,17 @@
     <string name="error_insufficient_firestore_permissions">Insufficient permissions to write to the database. Please contact support.</string>
     <string name="search_hint_mood_feed">"Search words/emotional states"</string>
     <string name="comment_hint_comments_page">"Your comment"</string>
-    <!-- TODO: Remove or change this placeholder text -->
     <string name="hello_blank_fragment">Hello blank fragment</string>
+    <string name="dummy_username">username</string>
+    <string name="delete_button_text">Delete</string>
+    <string name="edit_button_text">Edit</string>
+    <string name="mood_type_text">Mood</string>
+    <string name="social_situation_heading">Social Situation</string>
+    <string name="social_situation_box_text">Social Situation</string>
+    <string name="reason_heading">Reason</string>
+    <string name="reason_box_text">Reason</string>
+    <string name="photo_heading">Photo</string>
+    <string name="location_heading">Location</string>
+    <string name="error_loading_user">Error loading user</string>
+    <string name="no_user_id_provided">No user ID provided</string>
 </resources>
\ No newline at end of file


Reviews:
user: login: PossiblyAShrub
state: DISMISSED
author_association: NONE
submitted_at: 2025-03-20T15:48:09Z
user: login: adityadipakpatel
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-22T05:56:30Z
user: login: adityadipakpatel
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-22T05:56:46Z
user: login: adityadipakpatel
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-22T05:59:29Z
user: login: adityadipakpatel
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-22T06:07:46Z
user: login: siddhantgoel7
state: APPROVED
author_association: NONE
submitted_at: 2025-03-26T01:36:50Z
user: login: adityadipakpatel
body: Resolved
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-26T01:38:12Z

Review comments:
diff_hunk: @@ -69,59 +70,43 @@ public View onCreateView(@NonNull LayoutInflater inflater,
                              @Nullable Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_mood_details, container, false);
 
+        tvUsernameDisplay = view.findViewById(R.id.tvUsernameDisplay);
         toolbar = view.findViewById(R.id.moodDetailsToolbar);
         imageMoodIcon = view.findViewById(R.id.imageMoodIcon);
         tvMoodState = view.findViewById(R.id.tvMoodState);
         tvTriggerValue = view.findViewById(R.id.tvTriggerValue);
         tvSocialSituationValue = view.findViewById(R.id.tvSocialSituationValue);
         tvReasonValue = view.findViewById(R.id.tvReasonValue);
         ivMoodPhoto = view.findViewById(R.id.ivMoodPhoto);
+        tvUsernameDisplay = view.findViewById(R.id.tvUsernameDisplay);
path: code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
user: login: PossiblyAShrub
body: This doesn't need to be duplicated.
created_at: 2025-03-20T15:38:01Z
author_association: NONE
diff_hunk: @@ -154,20 +138,67 @@ private void bindMoodData(MoodEvent moodEvent) {
         if (photo != null) {
             ivMoodPhoto.setImageBitmap(photo);
         }
-    }
 
+        // Once we have userId, fetch user doc from Firestore
+        if (userId != null && !userId.isEmpty()) {
+            db.collection("users")
path: code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
user: login: PossiblyAShrub
body: The querying code should be moved to `UserProvider` if it is not already present there.
created_at: 2025-03-20T15:40:16Z
author_association: NONE
diff_hunk: @@ -154,20 +138,67 @@ private void bindMoodData(MoodEvent moodEvent) {
         if (photo != null) {
             ivMoodPhoto.setImageBitmap(photo);
         }
-    }
 
+        // Once we have userId, fetch user doc from Firestore
+        if (userId != null && !userId.isEmpty()) {
+            db.collection("users")
+                    .document(userId)
+                    .get()
+                    .addOnSuccessListener(documentSnapshot -> {
+                        if (documentSnapshot.exists()) {
+                            String username = documentSnapshot.getString("username");
+                            if (username == null) {
+                                username = documentSnapshot.getString("name");
+                            }
+
+                            if (username == null) {
+                                username = "UnknownUser";
+                            }
+                            tvUsernameDisplay.setText("@" + username);
+
+                            // Navigate to OtherUserProfile on click
+                            tvUsernameDisplay.setOnClickListener(v -> {
+                                Bundle args = new Bundle();
+                                args.putString("uid", userId);
+                                NavHostFragment.findNavController(MoodDetails.this)
+                                        .navigate(R.id.otherUserProfile, args);
+                            });
+
+                        } else {
+                            tvUsernameDisplay.setText("User not found");
+                        }
+                    })
+                    .addOnFailureListener(e -> {
+                        tvUsernameDisplay.setText("Error loading user");
+                    });
+        } else {
+            // If no user ID, show something else or keep blank
+            tvUsernameDisplay.setText("No user ID provided");
+        }
+
+        // 1) Get the current user from FirebaseAuth
+        FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
+        String currentUserUid = currentUser.getUid();
+
+        // 2) Compare the mood's owner (userId) to the current user’s UID
+        if (currentUser != null && userId != null && userId.equals(currentUserUid)) {
+            // The current user OWNS this mood => show the edit/delete buttons
+            btnEditMood.setVisibility(View.VISIBLE);
+            btnDeleteMood.setVisibility(View.VISIBLE);
+        } else {
+            // The current user does NOT own it => hide them
+            btnEditMood.setVisibility(View.GONE);
+            btnDeleteMood.setVisibility(View.GONE);
+        }
+    }
path: code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
user: login: PossiblyAShrub
body: We might want to move this to a separate method to keep things readable.
created_at: 2025-03-20T15:41:06Z
author_association: NONE
diff_hunk: @@ -0,0 +1,197 @@
+package com.kernelcrew.moodapp;
path: code/app/src/androidTest/java/com/kernelcrew/moodapp/MoodOwnershipTest.java
user: login: PossiblyAShrub
body: We are missing tests for the case where user 2 cannot view user 1's mood event edit and delete button.
created_at: 2025-03-20T15:48:03Z
author_association: NONE
diff_hunk: @@ -69,59 +70,43 @@ public View onCreateView(@NonNull LayoutInflater inflater,
                              @Nullable Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_mood_details, container, false);
 
+        tvUsernameDisplay = view.findViewById(R.id.tvUsernameDisplay);
         toolbar = view.findViewById(R.id.moodDetailsToolbar);
         imageMoodIcon = view.findViewById(R.id.imageMoodIcon);
         tvMoodState = view.findViewById(R.id.tvMoodState);
         tvTriggerValue = view.findViewById(R.id.tvTriggerValue);
         tvSocialSituationValue = view.findViewById(R.id.tvSocialSituationValue);
         tvReasonValue = view.findViewById(R.id.tvReasonValue);
         ivMoodPhoto = view.findViewById(R.id.ivMoodPhoto);
+        tvUsernameDisplay = view.findViewById(R.id.tvUsernameDisplay);
path: code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
user: login: adityadipakpatel
body: https://github.com/cmput301-w25/project-kernelcrew/pull/231/commits/1529e0b306fb23a6bb952c49491158954e033700
created_at: 2025-03-22T05:56:30Z
author_association: NONE
diff_hunk: @@ -154,20 +138,67 @@ private void bindMoodData(MoodEvent moodEvent) {
         if (photo != null) {
             ivMoodPhoto.setImageBitmap(photo);
         }
-    }
 
+        // Once we have userId, fetch user doc from Firestore
+        if (userId != null && !userId.isEmpty()) {
+            db.collection("users")
path: code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
user: login: adityadipakpatel
body: https://github.com/cmput301-w25/project-kernelcrew/pull/231/commits/d5e1ea903260b0cff0776d19cf6155f4efab3ca5
created_at: 2025-03-22T05:56:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,197 @@
+package com.kernelcrew.moodapp;
path: code/app/src/androidTest/java/com/kernelcrew/moodapp/MoodOwnershipTest.java
user: login: adityadipakpatel
body: yes i did have that but couldnt figure out at that time to make the tests pass, so i just deleted the 3rd test at that time
now, i took at lab 7's @AfterClass code to delete all users post completion of test 01, it should be fixed now

https://github.com/cmput301-w25/project-kernelcrew/pull/231/commits/4103ebc5c35ecb7bf404ac03a0f3fcbce5549d1a
created_at: 2025-03-22T05:59:28Z
author_association: NONE
diff_hunk: @@ -154,20 +138,67 @@ private void bindMoodData(MoodEvent moodEvent) {
         if (photo != null) {
             ivMoodPhoto.setImageBitmap(photo);
         }
-    }
 
+        // Once we have userId, fetch user doc from Firestore
+        if (userId != null && !userId.isEmpty()) {
+            db.collection("users")
+                    .document(userId)
+                    .get()
+                    .addOnSuccessListener(documentSnapshot -> {
+                        if (documentSnapshot.exists()) {
+                            String username = documentSnapshot.getString("username");
+                            if (username == null) {
+                                username = documentSnapshot.getString("name");
+                            }
+
+                            if (username == null) {
+                                username = "UnknownUser";
+                            }
+                            tvUsernameDisplay.setText("@" + username);
+
+                            // Navigate to OtherUserProfile on click
+                            tvUsernameDisplay.setOnClickListener(v -> {
+                                Bundle args = new Bundle();
+                                args.putString("uid", userId);
+                                NavHostFragment.findNavController(MoodDetails.this)
+                                        .navigate(R.id.otherUserProfile, args);
+                            });
+
+                        } else {
+                            tvUsernameDisplay.setText("User not found");
+                        }
+                    })
+                    .addOnFailureListener(e -> {
+                        tvUsernameDisplay.setText("Error loading user");
+                    });
+        } else {
+            // If no user ID, show something else or keep blank
+            tvUsernameDisplay.setText("No user ID provided");
+        }
+
+        // 1) Get the current user from FirebaseAuth
+        FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
+        String currentUserUid = currentUser.getUid();
+
+        // 2) Compare the mood's owner (userId) to the current user’s UID
+        if (currentUser != null && userId != null && userId.equals(currentUserUid)) {
+            // The current user OWNS this mood => show the edit/delete buttons
+            btnEditMood.setVisibility(View.VISIBLE);
+            btnDeleteMood.setVisibility(View.VISIBLE);
+        } else {
+            // The current user does NOT own it => hide them
+            btnEditMood.setVisibility(View.GONE);
+            btnDeleteMood.setVisibility(View.GONE);
+        }
+    }
path: code/app/src/main/java/com/kernelcrew/moodapp/ui/MoodDetails.java
user: login: adityadipakpatel
body: https://github.com/cmput301-w25/project-kernelcrew/pull/231/commits/f339fedf2e50f0427d23e3b1d0c70180bcb964eb
created_at: 2025-03-22T06:07:46Z
author_association: NONE

PR comments:
user: login: adityadipakpatel
created_at: 2025-03-22T07:07:48Z
body: @PossiblyAShrub 
This is now ready for review with all changes implemented as requested, the tests have also been updated
author_association: NONE
user: login: adityadipakpatel
created_at: 2025-03-25T22:07:52Z
body: > @PossiblyAShrub This is now ready for review with all changes implemented as requested, the tests have also been updated

@PossiblyAShrub @antonrou @siddhantgoel7 @jamesMckinnonSchool @TahaKamil 
Can anyone pleased review this PR?
author_association: NONE