diff --git a/app/src/androidTest/java/com/example/househomey/AddItemFragmentTest.java b/app/src/androidTest/java/com/example/househomey/AddItemFragmentTest.java index 2a4c649e..b21677cd 100644 --- a/app/src/androidTest/java/com/example/househomey/AddItemFragmentTest.java +++ b/app/src/androidTest/java/com/example/househomey/AddItemFragmentTest.java @@ -91,12 +91,6 @@ public void testSubmitWithRequiredEmpty() { onView(withId(R.id.add_item_date_layout)).check(matches(hasDescendant(withText(defaultErrorMsg)))); } - @Test - public void testBackButtonGoesToHomePage() { - onView(withId(R.id.add_item_back_button)).perform(click()); - onView(withId(R.id.item_list)).check(matches(isDisplayed())); - } - @Test public void testRoundCostTo2Decimals() { enterText(R.id.add_item_cost, "99.9852323324"); diff --git a/app/src/androidTest/java/com/example/househomey/EditItemFragmentTest.java b/app/src/androidTest/java/com/example/househomey/EditItemFragmentTest.java index 80f8a946..090cd998 100644 --- a/app/src/androidTest/java/com/example/househomey/EditItemFragmentTest.java +++ b/app/src/androidTest/java/com/example/househomey/EditItemFragmentTest.java @@ -43,13 +43,13 @@ public void navigateToEditItemFragment() throws Exception { // Create mock initial item in DB database.addTestItem(mockData); // Click to view the item page - waitFor(() -> onData(anything()) - .inAdapterView(withId(R.id.item_list)) - .atPosition(0) - .perform(click())); - // Click on edit button - waitFor(() -> onView(withId(R.id.edit_button)).perform(scrollTo())); - onView(withId(R.id.edit_button)).perform(click()); + waitFor(() -> { + onData(anything()) + .inAdapterView(withId(R.id.item_list)) + .atPosition(0) + .perform(click()); + onView(withId(R.id.edit_button)).perform(scrollTo(), click()); + }); } @Test diff --git a/app/src/androidTest/java/com/example/househomey/SignInFragmentTest.java b/app/src/androidTest/java/com/example/househomey/SignInFragmentTest.java index 8abdb4ed..ea4e1f74 100644 --- a/app/src/androidTest/java/com/example/househomey/SignInFragmentTest.java +++ b/app/src/androidTest/java/com/example/househomey/SignInFragmentTest.java @@ -20,6 +20,8 @@ import androidx.test.core.app.ActivityScenario; +import com.example.househomey.signin.SignInActivity; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/app/src/androidTest/java/com/example/househomey/SignUpFragmentTest.java b/app/src/androidTest/java/com/example/househomey/SignUpFragmentTest.java index bb39c167..8847b192 100644 --- a/app/src/androidTest/java/com/example/househomey/SignUpFragmentTest.java +++ b/app/src/androidTest/java/com/example/househomey/SignUpFragmentTest.java @@ -3,14 +3,11 @@ import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.action.ViewActions.clearText; import static androidx.test.espresso.action.ViewActions.click; -import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard; -import static androidx.test.espresso.action.ViewActions.pressImeActionButton; -import static androidx.test.espresso.action.ViewActions.typeText; +import static androidx.test.espresso.action.ViewActions.scrollTo; import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.matcher.ViewMatchers.hasErrorText; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; import static androidx.test.espresso.matcher.ViewMatchers.withId; - import static com.example.househomey.testUtils.TestHelpers.enterText; import static com.example.househomey.testUtils.TestHelpers.hasListLength; import static com.example.househomey.testUtils.TestHelpers.waitFor; @@ -18,9 +15,8 @@ import android.util.Log; import androidx.test.core.app.ActivityScenario; -import androidx.test.espresso.Espresso; -import com.google.android.gms.tasks.OnSuccessListener; +import com.example.househomey.signin.SignInActivity; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.FirebaseFirestore; @@ -32,9 +28,9 @@ import java.util.Objects; public class SignUpFragmentTest { + private final FirebaseAuth auth = FirebaseAuth.getInstance(); + private final FirebaseFirestore db = FirebaseFirestore.getInstance(); private ActivityScenario activityScenario; - FirebaseAuth auth = FirebaseAuth.getInstance(); - FirebaseFirestore db = FirebaseFirestore.getInstance(); @Before public void setUp() { @@ -45,16 +41,16 @@ public void setUp() { @Test public void testUsername() { - onView(withId(R.id.signup_username)).perform(typeText("antonio2")); + enterText(R.id.signup_username, "antonio2"); onView(withId(R.id.signup_username)).perform(clearText()); onView(withId(R.id.signup_username)).check(matches(hasErrorText("username cannot be empty"))); - onView(withId(R.id.signup_username)).perform(typeText("antonio2$")); + enterText(R.id.signup_username, "antonio2$"); onView(withId(R.id.signup_username)).check(matches(hasErrorText("only alphanumeric, underscore, or period"))); } @Test public void testEmail() { - onView(withId(R.id.signup_email)).perform(typeText("antonio2")); + enterText(R.id.signup_email, "antonio2"); onView(withId(R.id.signup_email)).check(matches(hasErrorText("not a valid email"))); onView(withId(R.id.signup_email)).perform(clearText()); onView(withId(R.id.signup_email)).check(matches(hasErrorText("email cannot be empty"))); @@ -62,35 +58,33 @@ public void testEmail() { @Test public void testConfirmedPassword() { - onView(withId(R.id.signup_password)).perform(typeText("123456")); - onView(withId(R.id.signup_confirm_password)).perform(typeText("12345")); + enterText(R.id.signup_password, "123456"); + enterText(R.id.signup_confirm_password, "12345"); onView(withId(R.id.signup_confirm_password)).check(matches(hasErrorText("passwords do not match"))); - onView(withId(R.id.signup_confirm_password)).perform(clearText()); - onView(withId(R.id.signup_confirm_password)).check(matches(hasErrorText("password cannot be empty"))); } @Test public void testRegister() { - onView(withId(R.id.signup_username)).perform(typeText("ESPRESSO_GENERAL_USER")); - onView(withId(R.id.signup_email)).perform(typeText("espresso_general_user@example.com")); + enterText(R.id.signup_username, "ESPRESSO_GENERAL_USER"); + enterText(R.id.signup_email, "espresso_general_user@example.com"); enterText(R.id.signup_password, "12345"); enterText(R.id.signup_confirm_password, "12345"); - onView(withId(R.id.signup_button)).perform(click()); + onView(withId(R.id.signup_button)).perform(scrollTo(), click()); waitFor(()->onView(withId(R.id.signup_username)).check(matches(hasErrorText("username already exists")))); enterText(R.id.signup_username, "a"); - onView(withId(R.id.signup_button)).perform(click()); + onView(withId(R.id.signup_button)).perform(scrollTo(), click()); waitFor(() -> onView(withId(R.id.signup_password)).check(matches(hasErrorText("The given password is invalid. [ Password should be at least 6 characters ]")))); onView(withId(R.id.signup_confirm_password)).check(matches(hasErrorText("The given password is invalid. [ Password should be at least 6 characters ]"))); enterText(R.id.signup_password, "123456"); enterText(R.id.signup_confirm_password, "123456"); - onView(withId(R.id.signup_button)).perform(click()); + onView(withId(R.id.signup_button)).perform(scrollTo(), click()); waitFor(() -> onView(withId(R.id.signup_email)).check(matches(hasErrorText("The email address is already in use by another account.")))); enterText(R.id.signup_email, "a@example.com"); - onView(withId(R.id.signup_button)).perform(click()); + onView(withId(R.id.signup_button)).perform(scrollTo(), click()); waitFor(() -> onView(withId(R.id.signin_username)).check(matches(isDisplayed()))); enterText(R.id.signin_username, "a"); enterText(R.id.signin_password, "123456"); - onView(withId(R.id.signin_button)).perform(click()); + onView(withId(R.id.signin_button)).perform(scrollTo(), click()); waitFor(()->hasListLength(0)); DocumentReference docRef = db.collection("user").document("a"); docRef.delete().addOnSuccessListener(a -> Objects.requireNonNull(auth.getCurrentUser()).delete().addOnSuccessListener(aVoid -> Log.d("ESP-TEST", "Firebase Auth user deleted successfully")) diff --git a/app/src/androidTest/java/com/example/househomey/testUtils/DatabaseSetupRule.java b/app/src/androidTest/java/com/example/househomey/testUtils/DatabaseSetupRule.java index 7e1c02da..c2939f51 100644 --- a/app/src/androidTest/java/com/example/househomey/testUtils/DatabaseSetupRule.java +++ b/app/src/androidTest/java/com/example/househomey/testUtils/DatabaseSetupRule.java @@ -2,14 +2,15 @@ import android.app.Activity; import android.content.Intent; -import android.os.Bundle; import android.util.Log; import androidx.annotation.NonNull; import androidx.test.core.app.ActivityScenario; -import com.example.househomey.Item; import com.example.househomey.MainActivity; +import com.example.househomey.item.Item; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.UserProfileChangeRequest; import com.google.firebase.firestore.CollectionReference; import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.FirebaseFirestore; @@ -21,6 +22,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -34,10 +36,12 @@ public class DatabaseSetupRule implements TestRule { private final String methodMatcher = "WithNewUser"; private final Class activityClass; + private final int dbTimeoutInSeconds = 30; + private final FirebaseAuth auth = FirebaseAuth.getInstance(); private DocumentReference userDoc; private boolean isDatabaseTest; - private final int dbTimeoutInSeconds = 30; private T activity; + private final String password = "123456"; public DatabaseSetupRule(Class activityClass) { this.activityClass = activityClass; @@ -50,12 +54,9 @@ public DatabaseSetupRule(Class activityClass) { * ex) proper page navigation, field validation, etc. */ public T setupActivity() { - Bundle userData = new Bundle(); - userData.putString("username", isDatabaseTest ? userDoc.getId() : "ESPRESSO_GENERAL_USER"); ActivityScenario.launch(activityClass).onActivity(activity -> { this.activity = activity; Intent intent = new Intent(activity, MainActivity.class); - intent.putExtra("userData", userData); activity.startActivity(intent); }); return activity; @@ -74,7 +75,8 @@ public void addTestItem(Map itemDetails) { // Ensure that mock data can be used to create a valid Item Item item; try { - item = new Item("", itemDetails, userDoc.collection("tag"), item1 -> {}); + item = new Item("", itemDetails, userDoc.collection("tag"), item1 -> { + }); } catch (NullPointerException e) { throw new RuntimeException("Mock data cannot create a valid Item: " + e.getMessage()); } @@ -105,6 +107,8 @@ public Statement apply(@NonNull Statement base, @NonNull Description description public void evaluate() throws Throwable { if (isDatabaseTest) { createTestUser(); + } else { + loginToGeneralTestUser(); } try { // Run the actual @Test method body @@ -118,41 +122,69 @@ public void evaluate() throws Throwable { }; } + public void loginToGeneralTestUser() { + CountDownLatch latch = new CountDownLatch(1); + auth.signInWithEmailAndPassword(getEmail(), password) + .addOnSuccessListener(authResult -> { + Log.d("ESP-TEST", "ESPRESSO_GENERAL_USER logged in successfully"); + latch.countDown(); + }) + .addOnFailureListener(e -> { + Log.e("ESP-TEST", "Login failed for ESPRESSO_GENERAL_USER: " + e.getMessage()); + latch.countDown(); + }); + + // Wait for the login to finish + try { + if (!latch.await(dbTimeoutInSeconds, TimeUnit.SECONDS)) { + throw new RuntimeException("Timeout waiting for user login."); + } + } catch (InterruptedException e) { + throw new RuntimeException("Timeout waiting for user login."); + } + } + + private void createTestUser() throws Exception { FirebaseFirestore db = FirebaseFirestore.getInstance(); CountDownLatch latch = new CountDownLatch(1); - db.collection("user").add(new HashMap()).addOnCompleteListener(task -> { - if (task.isSuccessful()) { - userDoc = task.getResult(); - latch.countDown(); - } else { - throw new RuntimeException("Could not generate a unique test user."); - } - }); - // Wait for user creation to finish + + // Create a unique Firebase Auth test user and document + String email = getEmail(); + auth.createUserWithEmailAndPassword(email, password) + .addOnSuccessListener(authResult -> auth.signInWithEmailAndPassword(email, password).addOnSuccessListener(aVoid -> { + UserProfileChangeRequest profileChangeRequest = new UserProfileChangeRequest.Builder().setDisplayName(email).build(); + auth.getCurrentUser().updateProfile(profileChangeRequest); + db.collection("user"). + document(email).set(new HashMap<>()).addOnSuccessListener(a -> { + userDoc = db.collection("user").document(email); + latch.countDown(); + }); + }).addOnFailureListener(e -> { + throw new RuntimeException("Could not create Firestore document for the test user: " + e.getMessage()); + })).addOnFailureListener(e -> { + throw new RuntimeException("Could not create Firebase Auth user for the test: " + e.getMessage()); + }); + + // Wait for user creation and Firestore document creation to finish if (!latch.await(dbTimeoutInSeconds, TimeUnit.SECONDS)) { throw new RuntimeException("Timeout waiting for test user creation."); } } - private void deleteTestUser() throws Exception { + private void deleteTestUser() { if (userDoc != null) { - CountDownLatch latch = new CountDownLatch(1); // Need to delete all nested collections before user doc can be deleted deleteNestedDocuments(userDoc.collection("item")); - userDoc.delete() - .addOnSuccessListener(aVoid -> { - Log.d("ESP-TEST", "Unique test user document deleted successfully"); - latch.countDown(); - }) - .addOnFailureListener(e -> { - Log.e("ESP-TEST", "Could not delete the unique test user: " + e.getMessage()); - latch.countDown(); - }); - // Wait for all deletions to finish - if (!latch.await(dbTimeoutInSeconds, TimeUnit.SECONDS)) { - throw new RuntimeException("Timeout waiting for test user deletion."); - } + deleteNestedDocuments(userDoc.collection("tag")); + + // Delete Firestore document + userDoc.delete().addOnSuccessListener(aVoid -> Log.d("ESP-TEST", "Unique test user document deleted successfully")) + .addOnFailureListener(e -> Log.e("ESP-TEST", "Could not delete the unique test user document: " + e.getMessage())); + + // Delete Firebase Auth test user + auth.getCurrentUser().delete().addOnSuccessListener(aVoid -> Log.d("ESP-TEST", "Firebase Auth user deleted successfully")) + .addOnFailureListener(e -> Log.e("ESP-TEST", "Could not delete Firebase Auth user: " + e.getMessage())); } } @@ -160,11 +192,14 @@ private void deleteNestedDocuments(CollectionReference collection) { collection.get().addOnSuccessListener(queryDocumentSnapshots -> { for (QueryDocumentSnapshot document : queryDocumentSnapshots) { DocumentReference docRef = document.getReference(); - docRef.delete() - .addOnSuccessListener(aVoid -> Log.d("ESP-TEST", "Nested document deleted successfully")) - .addOnFailureListener(e -> Log.e("ESP-TEST", "Could not delete nested document: " + e.getMessage())); + docRef.delete().addOnSuccessListener(aVoid -> Log.d("ESP-TEST", "Nested document deleted successfully")).addOnFailureListener(e -> Log.e("ESP-TEST", "Could not delete nested document: " + e.getMessage())); } }).addOnFailureListener(e -> Log.e("ESP-TEST", "Could not retrieve nested documents: " + e.getMessage())); } + + private String getEmail() { + if (isDatabaseTest) return UUID.randomUUID() + "@example.com"; + return "espresso_general_user@example.com"; + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8a610d58..ce5d9af2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ android:windowSoftInputMode="adjustPan" android:exported="true"> - diff --git a/app/src/main/java/com/example/househomey/MainActivity.java b/app/src/main/java/com/example/househomey/MainActivity.java index f6e5802a..447408a7 100644 --- a/app/src/main/java/com/example/househomey/MainActivity.java +++ b/app/src/main/java/com/example/househomey/MainActivity.java @@ -11,6 +11,10 @@ import androidx.fragment.app.FragmentManager; import com.example.househomey.form.AddItemFragment; +import com.example.househomey.home.HomeFragment; +import com.example.househomey.signin.SignInActivity; +import com.example.househomey.user.User; +import com.example.househomey.user.UserProfileFragment; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; @@ -106,5 +110,4 @@ public StorageReference getImageRef(String imageId) { .child(user.getUsername()) .child(imageId); } - } \ No newline at end of file diff --git a/app/src/main/java/com/example/househomey/filter/model/DateFilter.java b/app/src/main/java/com/example/househomey/filter/model/DateFilter.java index 1e6f3cde..dc54a162 100644 --- a/app/src/main/java/com/example/househomey/filter/model/DateFilter.java +++ b/app/src/main/java/com/example/househomey/filter/model/DateFilter.java @@ -1,6 +1,6 @@ package com.example.househomey.filter.model; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import java.util.ArrayList; import java.util.Date; diff --git a/app/src/main/java/com/example/househomey/filter/model/Filter.java b/app/src/main/java/com/example/househomey/filter/model/Filter.java index abf37b81..692b8548 100644 --- a/app/src/main/java/com/example/househomey/filter/model/Filter.java +++ b/app/src/main/java/com/example/househomey/filter/model/Filter.java @@ -1,6 +1,6 @@ package com.example.househomey.filter.model; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import java.io.Serializable; import java.util.ArrayList; diff --git a/app/src/main/java/com/example/househomey/filter/model/FilterCallback.java b/app/src/main/java/com/example/househomey/filter/model/FilterCallback.java index 988eaac8..02f838c6 100644 --- a/app/src/main/java/com/example/househomey/filter/model/FilterCallback.java +++ b/app/src/main/java/com/example/househomey/filter/model/FilterCallback.java @@ -1,11 +1,13 @@ package com.example.househomey.filter.model; +import com.example.househomey.home.HomeFragment; + import java.io.Serializable; /** * Callback interface for handling filter changes between fragments. - * @see com.example.househomey.HomeFragment + * @see HomeFragment */ public interface FilterCallback extends Serializable { void onFilterApplied(Filter filter); diff --git a/app/src/main/java/com/example/househomey/filter/model/KeywordFilter.java b/app/src/main/java/com/example/househomey/filter/model/KeywordFilter.java index 0baf39d5..1464f5ae 100644 --- a/app/src/main/java/com/example/househomey/filter/model/KeywordFilter.java +++ b/app/src/main/java/com/example/househomey/filter/model/KeywordFilter.java @@ -1,9 +1,7 @@ package com.example.househomey.filter.model; -import com.example.househomey.Item; -import com.google.android.material.chip.Chip; +import com.example.househomey.item.Item; -import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; diff --git a/app/src/main/java/com/example/househomey/filter/model/MakeFilter.java b/app/src/main/java/com/example/househomey/filter/model/MakeFilter.java index 264fbf97..7c8fcee4 100644 --- a/app/src/main/java/com/example/househomey/filter/model/MakeFilter.java +++ b/app/src/main/java/com/example/househomey/filter/model/MakeFilter.java @@ -1,8 +1,7 @@ package com.example.househomey.filter.model; -import com.example.househomey.Item; +import com.example.househomey.item.Item; -import java.io.Serializable; import java.util.ArrayList; import java.util.stream.Collectors; diff --git a/app/src/main/java/com/example/househomey/filter/model/TagFilter.java b/app/src/main/java/com/example/househomey/filter/model/TagFilter.java index a2aba9c6..f02d7b3b 100644 --- a/app/src/main/java/com/example/househomey/filter/model/TagFilter.java +++ b/app/src/main/java/com/example/househomey/filter/model/TagFilter.java @@ -1,13 +1,10 @@ package com.example.househomey.filter.model; -import android.util.Log; - -import com.example.househomey.Item; +import com.example.househomey.item.Item; import com.example.househomey.tags.Tag; import java.io.Serializable; import java.util.ArrayList; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; diff --git a/app/src/main/java/com/example/househomey/filter/ui/TagFilterFragment.java b/app/src/main/java/com/example/househomey/filter/ui/TagFilterFragment.java index 4ce64cbe..bf4bddc2 100644 --- a/app/src/main/java/com/example/househomey/filter/ui/TagFilterFragment.java +++ b/app/src/main/java/com/example/househomey/filter/ui/TagFilterFragment.java @@ -10,30 +10,18 @@ import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; -import android.app.Dialog; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.example.househomey.R; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import com.example.househomey.Item; import com.example.househomey.R; import com.example.househomey.filter.model.FilterCallback; -import com.example.househomey.filter.model.MakeFilter; import com.example.househomey.filter.model.TagFilter; import com.example.househomey.tags.Tag; import com.google.android.material.chip.Chip; import com.google.android.material.chip.ChipGroup; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; /** diff --git a/app/src/main/java/com/example/househomey/form/AddItemFragment.java b/app/src/main/java/com/example/househomey/form/AddItemFragment.java index 02aff01b..c387f12c 100644 --- a/app/src/main/java/com/example/househomey/form/AddItemFragment.java +++ b/app/src/main/java/com/example/househomey/form/AddItemFragment.java @@ -12,13 +12,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import com.example.househomey.R; import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputLayout; -import java.util.Map; - /** * This fragment is responsible for creating and loading to the database a new item * diff --git a/app/src/main/java/com/example/househomey/form/EditItemFragment.java b/app/src/main/java/com/example/househomey/form/EditItemFragment.java index 3c59c184..3459038e 100644 --- a/app/src/main/java/com/example/househomey/form/EditItemFragment.java +++ b/app/src/main/java/com/example/househomey/form/EditItemFragment.java @@ -2,28 +2,22 @@ import static com.example.househomey.utils.FragmentUtils.formatDate; import static com.example.househomey.utils.FragmentUtils.goBack; -import static com.example.househomey.utils.FragmentUtils.navigateToFragmentPage; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.core.os.BundleCompat; -import com.example.househomey.Item; -import com.example.househomey.MainActivity; import com.example.househomey.R; -import com.example.househomey.ViewItemFragment; +import com.example.househomey.item.Item; import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textview.MaterialTextView; import java.io.Serializable; -import java.util.Map; -import java.util.concurrent.atomic.AtomicBoolean; /** * This fragment is responsible for editing an existing item in the database. diff --git a/app/src/main/java/com/example/househomey/form/ItemFormFragment.java b/app/src/main/java/com/example/househomey/form/ItemFormFragment.java index 06f074aa..96c254f7 100644 --- a/app/src/main/java/com/example/househomey/form/ItemFormFragment.java +++ b/app/src/main/java/com/example/househomey/form/ItemFormFragment.java @@ -19,7 +19,7 @@ import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import com.example.househomey.MainActivity; import com.example.househomey.R; import com.example.househomey.utils.FragmentUtils; @@ -38,7 +38,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.Map; import java.util.UUID; /** diff --git a/app/src/main/java/com/example/househomey/DeleteItemsFragment.java b/app/src/main/java/com/example/househomey/home/DeleteItemsFragment.java similarity index 94% rename from app/src/main/java/com/example/househomey/DeleteItemsFragment.java rename to app/src/main/java/com/example/househomey/home/DeleteItemsFragment.java index 3cb22988..aad0aca5 100644 --- a/app/src/main/java/com/example/househomey/DeleteItemsFragment.java +++ b/app/src/main/java/com/example/househomey/home/DeleteItemsFragment.java @@ -1,16 +1,17 @@ -package com.example.househomey; +package com.example.househomey.home; import android.app.AlertDialog; import android.app.Dialog; -import android.content.Context; import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.househomey.R; +import com.example.househomey.item.Item; + import java.util.ArrayList; /** diff --git a/app/src/main/java/com/example/househomey/HomeFragment.java b/app/src/main/java/com/example/househomey/home/HomeFragment.java similarity index 98% rename from app/src/main/java/com/example/househomey/HomeFragment.java rename to app/src/main/java/com/example/househomey/home/HomeFragment.java index 555e5538..798f55ba 100644 --- a/app/src/main/java/com/example/househomey/HomeFragment.java +++ b/app/src/main/java/com/example/househomey/home/HomeFragment.java @@ -1,4 +1,4 @@ -package com.example.househomey; +package com.example.househomey.home; import static com.example.househomey.utils.FragmentUtils.navigateToFragmentPage; @@ -9,7 +9,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; -import android.widget.CompoundButton; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.TextView; @@ -19,6 +18,8 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import com.example.househomey.MainActivity; +import com.example.househomey.R; import com.example.househomey.filter.model.DateFilter; import com.example.househomey.filter.model.KeywordFilter; import com.example.househomey.filter.model.MakeFilter; @@ -29,6 +30,8 @@ import com.example.househomey.filter.ui.KeywordFilterFragment; import com.example.househomey.filter.ui.MakeFilterFragment; import com.example.househomey.filter.ui.TagFilterFragment; +import com.example.househomey.item.Item; +import com.example.househomey.item.ItemAdapter; import com.example.househomey.sort.CostComparator; import com.example.househomey.sort.DateComparator; import com.example.househomey.sort.DescriptionComparator; diff --git a/app/src/main/java/com/example/househomey/SelectFragment.java b/app/src/main/java/com/example/househomey/home/SelectFragment.java similarity index 97% rename from app/src/main/java/com/example/househomey/SelectFragment.java rename to app/src/main/java/com/example/househomey/home/SelectFragment.java index 209913fe..a6f28328 100644 --- a/app/src/main/java/com/example/househomey/SelectFragment.java +++ b/app/src/main/java/com/example/househomey/home/SelectFragment.java @@ -1,4 +1,4 @@ -package com.example.househomey; +package com.example.househomey.home; import static com.example.househomey.utils.FragmentUtils.deletePhotosFromCloud; import static com.example.househomey.utils.FragmentUtils.goBack; @@ -20,6 +20,10 @@ import androidx.core.os.BundleCompat; import androidx.fragment.app.Fragment; +import com.example.househomey.MainActivity; +import com.example.househomey.R; +import com.example.househomey.item.Item; +import com.example.househomey.item.ItemAdapter; import com.example.househomey.tags.Tag; import com.example.househomey.tags.ApplyTagFragment; import com.google.firebase.firestore.CollectionReference; diff --git a/app/src/main/java/com/example/househomey/Item.java b/app/src/main/java/com/example/househomey/item/Item.java similarity index 99% rename from app/src/main/java/com/example/househomey/Item.java rename to app/src/main/java/com/example/househomey/item/Item.java index 64630977..208cae9a 100644 --- a/app/src/main/java/com/example/househomey/Item.java +++ b/app/src/main/java/com/example/househomey/item/Item.java @@ -1,4 +1,4 @@ -package com.example.househomey; +package com.example.househomey.item; import android.os.Parcel; import android.os.Parcelable; diff --git a/app/src/main/java/com/example/househomey/ItemAdapter.java b/app/src/main/java/com/example/househomey/item/ItemAdapter.java similarity index 98% rename from app/src/main/java/com/example/househomey/ItemAdapter.java rename to app/src/main/java/com/example/househomey/item/ItemAdapter.java index bd5e3b0e..47e2f7c2 100644 --- a/app/src/main/java/com/example/househomey/ItemAdapter.java +++ b/app/src/main/java/com/example/househomey/item/ItemAdapter.java @@ -1,4 +1,4 @@ -package com.example.househomey; +package com.example.househomey.item; import static com.example.househomey.utils.FragmentUtils.formatDate; import static com.example.househomey.utils.FragmentUtils.navigateToFragmentPage; @@ -10,13 +10,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import android.widget.Button; import android.widget.CheckBox; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.example.househomey.R; import com.example.househomey.tags.Tag; import com.example.househomey.utils.FragmentUtils; import com.google.android.material.chip.Chip; diff --git a/app/src/main/java/com/example/househomey/ViewItemFragment.java b/app/src/main/java/com/example/househomey/item/ViewItemFragment.java similarity index 97% rename from app/src/main/java/com/example/househomey/ViewItemFragment.java rename to app/src/main/java/com/example/househomey/item/ViewItemFragment.java index 4ba87134..e8720d03 100644 --- a/app/src/main/java/com/example/househomey/ViewItemFragment.java +++ b/app/src/main/java/com/example/househomey/item/ViewItemFragment.java @@ -1,4 +1,4 @@ -package com.example.househomey; +package com.example.househomey.item; import static com.example.househomey.utils.FragmentUtils.deletePhotosFromCloud; import static com.example.househomey.utils.FragmentUtils.goBack; @@ -18,9 +18,13 @@ import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; +import com.example.househomey.MainActivity; +import com.example.househomey.R; import com.example.househomey.form.EditItemFragment; import com.example.househomey.form.ViewPhotoAdapter; +import com.example.househomey.home.HomeFragment; +import com.example.househomey.item.Item; import com.example.househomey.tags.Tag; import com.example.househomey.tags.ApplyTagFragment; import com.example.househomey.utils.FragmentUtils; diff --git a/app/src/main/java/com/example/househomey/signin/SignInActivity.java b/app/src/main/java/com/example/househomey/signin/SignInActivity.java new file mode 100644 index 00000000..9cd56af2 --- /dev/null +++ b/app/src/main/java/com/example/househomey/signin/SignInActivity.java @@ -0,0 +1,33 @@ +package com.example.househomey.signin; + +import static com.example.househomey.utils.FragmentUtils.navigateToFragmentPage; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.example.househomey.R; + + +/** + * SignInActivity of the application, handles setting up the sign in page + * @author Antonio Lech Martin-Ozimek + */ +public class SignInActivity extends AppCompatActivity { + + /** + * This launches the SignInActivity which is the basis for both the SignInFragment + * and the SignUpFragment. Once the user has signed in we exit this activity. + * + * @param savedInstanceState A Bundle containing the activity's previously frozen state, or null + * if there was none. + */ + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_signin); + navigateToFragmentPage(this, new SignInFragment(), R.id.fragmentContainerSignIn); + + } +} diff --git a/app/src/main/java/com/example/househomey/signin/SignInFragment.java b/app/src/main/java/com/example/househomey/signin/SignInFragment.java new file mode 100644 index 00000000..e2f31ece --- /dev/null +++ b/app/src/main/java/com/example/househomey/signin/SignInFragment.java @@ -0,0 +1,175 @@ +package com.example.househomey.signin; + +import static com.example.househomey.utils.FragmentUtils.navigateToFragmentPage; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.example.househomey.MainActivity; +import com.example.househomey.R; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.firestore.CollectionReference; +import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.FirebaseFirestore; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * SignInFragment of the application, defines the sign in page + * and links to the SignUpFragment. Deals with validating sign in + * information from user. + * @author Antonio Lech Martin-Ozimek + */ +public class SignInFragment extends Fragment { + private EditText usernameEdittext; + private EditText passwordEdittext; + private CollectionReference userRef; + private DocumentReference docRef; + private Button loginButton; + // Define a regex pattern for lowercase alphanumeric with underscores or periods + private final String regex = "^[a-zA-Z0-9_.]+$"; + private FirebaseAuth auth; + private String username; + private String password; + + /** + * Overrides the default implementation to inflate the layout for the SignInFragment, + * initialize Firebase Authentication, and set up UI components such as username and password + * fields, login button, and redirection text views. Handles user input for login, validates + * username and password, and initiates the login process using Firebase Authentication. + * Navigates to the Sign Up fragment when the redirection link is clicked. + * + * @param inflater The LayoutInflater object that can be used to inflate any views in + * the fragment. + * @param container If non-null, this is the parent view that the fragment's UI should + * be attached to. The fragment should not add the view itself, but + * this can be used to generate the LayoutParams of the view. + * @param savedInstanceState If non-null, this fragment is being re-constructed from a previous + * saved state as given here. + * @return The root view of the fragment's layout. + */ + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_user_signin, container, false); + + // Initialize Firebase Auth + auth = FirebaseAuth.getInstance(); + auth.signOut(); + + usernameEdittext = rootView.findViewById(R.id.signin_username); + passwordEdittext = rootView.findViewById(R.id.signin_password); + loginButton = rootView.findViewById(R.id.signin_button); + TextView signInRedirect = rootView.findViewById(R.id.signin_redirect); + + loginButton.setOnClickListener(v -> { + loginButton.setBackgroundResource(R.drawable.signin_button_clicked); + loginButton.setTextColor(getResources().getColor(R.color.creme, rootView.getContext().getTheme())); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + loginButton.setBackgroundResource(R.drawable.signin_button_unclicked); + loginButton.setTextColor(getResources().getColor(R.color.brown, rootView.getContext().getTheme())); + }, 150); + + usernameEdittext.setError(null); + passwordEdittext.setError(null); + + userRef = FirebaseFirestore.getInstance().collection("user"); + + if (!username.isEmpty() & !password.isEmpty()) { + docRef = userRef.document(username); + docRef.get().addOnSuccessListener(task -> { + Map document = task.getData(); + if (document != null) { + String email = (String) document.get("email"); + if (email == null) { + usernameEdittext.setError("no email associated with this account"); + } else { + auth.signInWithEmailAndPassword(email, password).addOnSuccessListener(task1 -> { + FirebaseUser user = task1.getUser(); + if (user == null) { + usernameEdittext.setError("database error"); + passwordEdittext.setError("database error"); + } else { + Activity activity = getActivity(); + if (activity != null) { + Intent intent = new Intent(activity, MainActivity.class); + activity.startActivity(intent); + activity.finish(); + } + } + }); + } + } else { + usernameEdittext.setError("username or password not recognized"); + passwordEdittext.setError("username or password not recognized"); + } + }); + } else { + usernameEdittext.setError("username or password empty"); + passwordEdittext.setError("username or password empty"); + } + + }); + + usernameEdittext.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void afterTextChanged(Editable editable) { + username = editable.toString().trim(); + + // Use Pattern and Matcher to check if the username matches the pattern + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(username); + + if (username.isEmpty()) { + usernameEdittext.setError("username cannot be empty"); + } else if(!matcher.matches()) { + usernameEdittext.setError("only alphanumeric, underscore, or period"); + } + } + }); + + passwordEdittext.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void afterTextChanged(Editable editable) { + password = editable.toString().trim(); + if (password.isEmpty()) { + passwordEdittext.setError("password cannot be empty"); + } + } + }); + + signInRedirect.setOnClickListener(v -> navigateToFragmentPage(getActivity(), new SignUpFragment(), R.id.fragmentContainerSignIn)); + + return rootView; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/househomey/signin/SignUpFragment.java b/app/src/main/java/com/example/househomey/signin/SignUpFragment.java new file mode 100644 index 00000000..423c38ff --- /dev/null +++ b/app/src/main/java/com/example/househomey/signin/SignUpFragment.java @@ -0,0 +1,262 @@ +package com.example.househomey.signin; + +import static com.example.househomey.utils.FragmentUtils.navigateToFragmentPage; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.example.househomey.R; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseAuthUserCollisionException; +import com.google.firebase.auth.FirebaseAuthWeakPasswordException; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.UserProfileChangeRequest; +import com.google.firebase.firestore.CollectionReference; +import com.google.firebase.firestore.FirebaseFirestore; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SignUpFragment extends Fragment { + private EditText usernameEdittext; + private EditText emailEdittext; + private EditText passwordEdittext; + private EditText confirmPasswordEdittext; + private String confirmedPassword; + private CollectionReference collRef; + private Button signupButton; + // Define a regex pattern for lowercase alphanumeric with underscores or periods + private final String emailRegex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$"; + private final String usernameRegex = "^[a-zA-Z0-9_.]+$"; + private FirebaseAuth auth; + private String username; + private String email; + private String password; + + /** + * Overrides the default implementation to inflate the layout for the SignUpFragment, + * initialize Firebase Authentication, and set up UI components such as username, email, + * password, and confirmation fields, and the registration button. Handles user input for + * registration, validates user inputs, creates a new user account using Firebase + * Authentication, updates the user profile, and stores user data in Firestore. Navigates + * to the SignInFragment when the redirection link is clicked. + * + * @param inflater The LayoutInflater object that can be used to inflate any views in + * the fragment. + * @param container If non-null, this is the parent view that the fragment's UI should + * be attached to. The fragment should not add the view itself, but + * this can be used to generate the LayoutParams of the view. + * @param savedInstanceState If non-null, this fragment is being re-constructed from a previous + * saved state as given here. + * @return The root view of the fragment's layout. + */ + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_user_signup, container, false); + + // Initialize Firebase Auth + auth = FirebaseAuth.getInstance(); + + usernameEdittext = rootView.findViewById(R.id.signup_username); + emailEdittext = rootView.findViewById(R.id.signup_email); + passwordEdittext = rootView.findViewById(R.id.signup_password); + confirmPasswordEdittext = rootView.findViewById(R.id.signup_confirm_password); + signupButton = rootView.findViewById(R.id.signup_button); + + TextView signInRedirect = rootView.findViewById(R.id.signin_redirect); + + signupButton.setOnClickListener(v -> { + signupButton.setBackgroundResource(R.drawable.signin_button_clicked); + signupButton.setTextColor(getResources().getColor(R.color.creme, rootView.getContext().getTheme())); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + signupButton.setBackgroundResource(R.drawable.signin_button_unclicked); + signupButton.setTextColor(getResources().getColor(R.color.brown, rootView.getContext().getTheme())); + }, 150); + + collRef = FirebaseFirestore.getInstance().collection("user"); + if (confirmPassword()) { + collRef.document(username).get().addOnSuccessListener(task -> { + if (task.getData() == null) { + UserProfileChangeRequest profileChangeRequest = + new UserProfileChangeRequest.Builder().setDisplayName(username).build(); + auth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(task1 -> { + if (task1.isSuccessful()) { + FirebaseUser user = auth.getCurrentUser(); + if (user == null) { + emailEdittext.setError("database error"); + usernameEdittext.setError("database error"); + } else { + user.updateProfile(profileChangeRequest).addOnSuccessListener(task12 -> { + Map fields = new HashMap<>(); + fields.put("email", user.getEmail()); + collRef.document(user.getDisplayName()).set(fields).addOnCompleteListener(task2 -> { + if (task2.isSuccessful()) { + navigateToFragmentPage(getActivity(), new SignInFragment(), R.id.fragmentContainerSignIn); + } else { + usernameEdittext.setError(task1.getException().getMessage()); + passwordEdittext.setError(task1.getException().getMessage()); + } + }); + }); + } + } else if (task1.getException() instanceof FirebaseAuthUserCollisionException) + { + //If email already registered. + emailEdittext.setError(task1.getException().getMessage()); + + } else if (task1.getException() instanceof FirebaseAuthWeakPasswordException) { + //if password not 'stronger' + passwordEdittext.setError(task1.getException().getMessage()); + confirmPasswordEdittext.setError(task1.getException().getMessage()); + } + }); + } else { + usernameEdittext.setError("username already exists"); + } + }); + } + }); + + usernameEdittext.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void afterTextChanged(Editable editable) { + username = editable.toString().trim(); + + // Use Pattern and Matcher to check if the username matches the pattern + Pattern pattern = Pattern.compile(usernameRegex); + Matcher matcher = pattern.matcher(username); + + if (username.isEmpty()) { + usernameEdittext.setError("username cannot be empty"); + } else if(!matcher.matches()) { + usernameEdittext.setError("only alphanumeric, underscore, or period"); + } + } + }); + + emailEdittext.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable editable) { + email = editable.toString().trim(); + Pattern pattern = Pattern.compile(emailRegex); + Matcher matcher = pattern.matcher(email); + + if (email.isEmpty()) { + emailEdittext.setError("email cannot be empty"); + } else if (!matcher.matches()) { + emailEdittext.setError("not a valid email"); + } + } + }); + + passwordEdittext.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + + @Override + public void afterTextChanged(Editable editable) { + password = editable.toString().trim(); + if (password.isEmpty()) { + passwordEdittext.setError("password cannot be empty"); + } else if (!password.equals(confirmedPassword)) { + confirmPasswordEdittext.setError("passwords do not match"); + passwordEdittext.setError("passwords do not match"); + } else { + confirmPasswordEdittext.setError(null); + passwordEdittext.setError(null); + } + } + }); + + confirmPasswordEdittext.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable editable) { + confirmedPassword = editable.toString().trim(); + if(!confirmedPassword.equals(password)) { + confirmPasswordEdittext.setError("passwords do not match"); + passwordEdittext.setError("passwords do not match"); + } else if (confirmedPassword.isEmpty()) { + confirmPasswordEdittext.setError("cannot be empty"); + } else { + confirmPasswordEdittext.setError(null); + passwordEdittext.setError(null); + } + } + }); + + signInRedirect.setOnClickListener(v -> navigateToFragmentPage(getActivity(), new SignInFragment(), R.id.fragmentContainerSignIn)); + return rootView; + } + + /** + * Validates the user's registration inputs, including username, email, password, and confirmation + * password. Checks for non-empty and error-free inputs, ensuring that the confirmation password + * matches the original password. Clears any previous error messages associated with the fields + * upon successful validation. + * + * @return {@code true} if all registration inputs are valid; {@code false} otherwise. + */ + private boolean confirmPassword() { + if (!password.isEmpty() & !username.isEmpty() & !email.isEmpty()){ + if (passwordEdittext.getError() == null & usernameEdittext.getError() == null & emailEdittext.getError() == null) { + if(confirmedPassword.equals(password)){ + if(confirmPasswordEdittext.getError() == null) { + usernameEdittext.setError(null); + emailEdittext.setError(null); + passwordEdittext.setError(null); + confirmPasswordEdittext.setError(null); + return true; + } + } else { + passwordEdittext.setError("passwords do not match"); + confirmPasswordEdittext.setError("passwords do not match"); + } + } + } + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/househomey/sort/CostComparator.java b/app/src/main/java/com/example/househomey/sort/CostComparator.java index f1d684ec..ece98e48 100644 --- a/app/src/main/java/com/example/househomey/sort/CostComparator.java +++ b/app/src/main/java/com/example/househomey/sort/CostComparator.java @@ -1,8 +1,6 @@ package com.example.househomey.sort; -import com.example.househomey.Item; - -import org.checkerframework.checker.units.qual.C; +import com.example.househomey.item.Item; import java.util.Comparator; diff --git a/app/src/main/java/com/example/househomey/sort/DateComparator.java b/app/src/main/java/com/example/househomey/sort/DateComparator.java index 2c0f78e3..3cb66091 100644 --- a/app/src/main/java/com/example/househomey/sort/DateComparator.java +++ b/app/src/main/java/com/example/househomey/sort/DateComparator.java @@ -1,6 +1,6 @@ package com.example.househomey.sort; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import java.util.Comparator; diff --git a/app/src/main/java/com/example/househomey/sort/DescriptionComparator.java b/app/src/main/java/com/example/househomey/sort/DescriptionComparator.java index 3df806d0..419da134 100644 --- a/app/src/main/java/com/example/househomey/sort/DescriptionComparator.java +++ b/app/src/main/java/com/example/househomey/sort/DescriptionComparator.java @@ -1,6 +1,6 @@ package com.example.househomey.sort; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import java.util.Comparator; diff --git a/app/src/main/java/com/example/househomey/sort/MakeComparator.java b/app/src/main/java/com/example/househomey/sort/MakeComparator.java index 3ef3ffd3..42076bdf 100644 --- a/app/src/main/java/com/example/househomey/sort/MakeComparator.java +++ b/app/src/main/java/com/example/househomey/sort/MakeComparator.java @@ -1,6 +1,6 @@ package com.example.househomey.sort; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import java.util.Comparator; diff --git a/app/src/main/java/com/example/househomey/sort/TagComparator.java b/app/src/main/java/com/example/househomey/sort/TagComparator.java index f8ba6bbe..b9d00406 100644 --- a/app/src/main/java/com/example/househomey/sort/TagComparator.java +++ b/app/src/main/java/com/example/househomey/sort/TagComparator.java @@ -1,6 +1,6 @@ package com.example.househomey.sort; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import com.example.househomey.tags.Tag; import java.util.Comparator; diff --git a/app/src/main/java/com/example/househomey/tags/ApplyTagFragment.java b/app/src/main/java/com/example/househomey/tags/ApplyTagFragment.java index 8991475a..2a7fec4f 100644 --- a/app/src/main/java/com/example/househomey/tags/ApplyTagFragment.java +++ b/app/src/main/java/com/example/househomey/tags/ApplyTagFragment.java @@ -9,7 +9,7 @@ import androidx.annotation.NonNull; import androidx.core.os.BundleCompat; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import com.example.househomey.MainActivity; import com.example.househomey.R; import com.example.househomey.utils.FragmentUtils; diff --git a/app/src/main/java/com/example/househomey/tags/ManageTagFragment.java b/app/src/main/java/com/example/househomey/tags/ManageTagFragment.java index f8e87f63..e6e569e5 100644 --- a/app/src/main/java/com/example/househomey/tags/ManageTagFragment.java +++ b/app/src/main/java/com/example/househomey/tags/ManageTagFragment.java @@ -10,7 +10,7 @@ import androidx.annotation.NonNull; import androidx.core.os.BundleCompat; -import com.example.househomey.Item; +import com.example.househomey.item.Item; import com.example.househomey.MainActivity; import com.example.househomey.R; import com.example.househomey.utils.FragmentUtils; diff --git a/app/src/main/java/com/example/househomey/tags/Tag.java b/app/src/main/java/com/example/househomey/tags/Tag.java index 7278dc89..f8dcb0eb 100644 --- a/app/src/main/java/com/example/househomey/tags/Tag.java +++ b/app/src/main/java/com/example/househomey/tags/Tag.java @@ -5,13 +5,7 @@ import androidx.annotation.NonNull; -import com.example.househomey.Item; - -import java.io.Serializable; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/app/src/main/java/com/example/househomey/User.java b/app/src/main/java/com/example/househomey/user/User.java similarity index 97% rename from app/src/main/java/com/example/househomey/User.java rename to app/src/main/java/com/example/househomey/user/User.java index 070f86c9..bec68ae1 100644 --- a/app/src/main/java/com/example/househomey/User.java +++ b/app/src/main/java/com/example/househomey/user/User.java @@ -1,4 +1,4 @@ -package com.example.househomey; +package com.example.househomey.user; import com.google.firebase.firestore.CollectionReference; import com.google.firebase.firestore.FirebaseFirestore; diff --git a/app/src/main/java/com/example/househomey/user/UserProfileFragment.java b/app/src/main/java/com/example/househomey/user/UserProfileFragment.java new file mode 100644 index 00000000..e9f718df --- /dev/null +++ b/app/src/main/java/com/example/househomey/user/UserProfileFragment.java @@ -0,0 +1,65 @@ +package com.example.househomey.user; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.example.househomey.R; +import com.example.househomey.signin.SignInActivity; +import com.google.firebase.auth.FirebaseAuth; + +public class UserProfileFragment extends Fragment { + private final FirebaseAuth auth = FirebaseAuth.getInstance(); + + /** + * Called to create the user profile fragment's view hierarchy. Inflates the fragment layout from + * the specified XML resource, populates the item list from the bundle arguments, and sets up UI + * elements such as the username TextView and logout button. + * + * @param inflater The LayoutInflater object that can be used to inflate any views in + * the fragment. + * @param container If non-null, this is the parent view that the fragment's UI should be + * attached to. The fragment should not add the view itself, but this can + * be used to generate the LayoutParams of the view. + * @param savedInstanceState If non-null, this fragment is being re-constructed from a previous + * saved state as given here. + * @return The root view of the fragment's layout hierarchy. + */ + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_user_profile, container, false); + + // Set the user's info + ((TextView) rootView.findViewById(R.id.user_profile_username)).setText(auth.getCurrentUser().getDisplayName()); + ((TextView) rootView.findViewById(R.id.user_profile_email)).setText(auth.getCurrentUser().getEmail()); + + final Button logoutButton = rootView.findViewById(R.id.user_profile_logout_button); + logoutButton.setOnClickListener(v -> { + FirebaseAuth.getInstance().signOut(); + logoutButton.setBackgroundResource(R.drawable.logout_button_clicked); + logoutButton.setTextColor(getResources().getColor(R.color.creme, rootView.getContext().getTheme())); + new Handler(Looper.getMainLooper()).postDelayed(() -> { + logoutButton.setBackgroundResource(R.drawable.logout_button_unclicked); + logoutButton.setTextColor(getResources().getColor(R.color.black, rootView.getContext().getTheme())); + }, 100); + Activity activity = getActivity(); + if (activity != null) { + Intent intent = new Intent(activity, SignInActivity.class); + activity.startActivity(intent); + } + }); + + return rootView; + } +} diff --git a/app/src/main/res/layout/activity_signin.xml b/app/src/main/res/layout/activity_signin.xml index ca725119..9a43dcd4 100644 --- a/app/src/main/res/layout/activity_signin.xml +++ b/app/src/main/res/layout/activity_signin.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/creme" - tools:context=".SignInActivity"> + tools:context=".signin.SignInActivity"> + android:textSize="16dp" + android:textStyle="bold" + android:text="john_doe"> + + + + + + Class DeleteItemsFragmentjava.lang.Object
androidx.fragment.app.Fragment
androidx.fragment.app.DialogFragment -
com.example.househomey.DeleteItemsFragment
+
com.example.househomey.home.DeleteItemsFragment
@@ -135,7 +135,7 @@

Constructor Summary

Constructor
Description
-
DeleteItemsFragment(DeleteItemsFragment.DeleteCallBack deleteCallBack, +
Constructor for the Delete Items confirmation dialog fragment
@@ -183,7 +183,7 @@

Methods inherited from cl

Constructor Details

  • -
    +

    DeleteItemsFragment

    public DeleteItemsFragment(DeleteItemsFragment.DeleteCallBack deleteCallBack, ArrayList<Item> selectedItems)
    diff --git a/javadoc/com/example/househomey/HomeFragment.html b/javadoc/com/example/househomey/HomeFragment.html index 7aed1de3..58286261 100644 --- a/javadoc/com/example/househomey/HomeFragment.html +++ b/javadoc/com/example/househomey/HomeFragment.html @@ -74,7 +74,7 @@

    Class HomeFragment

java.lang.Object
androidx.fragment.app.Fragment -
com.example.househomey.HomeFragment
+
com.example.househomey.home.HomeFragment
diff --git a/javadoc/com/example/househomey/Item.html b/javadoc/com/example/househomey/Item.html index 934482cd..a4d678ce 100644 --- a/javadoc/com/example/househomey/Item.html +++ b/javadoc/com/example/househomey/Item.html @@ -73,7 +73,7 @@

Class Item

java.lang.Object -
com.example.househomey.Item
+
com.example.househomey.item.Item
@@ -92,7 +92,7 @@

Class Item

diff --git a/javadoc/com/example/househomey/ItemAdapter.html b/javadoc/com/example/househomey/ItemAdapter.html index 288f8aab..e9400b62 100644 --- a/javadoc/com/example/househomey/ItemAdapter.html +++ b/javadoc/com/example/househomey/ItemAdapter.html @@ -75,7 +75,7 @@

Class ItemAdapter

java.lang.Object
android.widget.BaseAdapter
android.widget.ArrayAdapter<Item> -
com.example.househomey.ItemAdapter
+
com.example.househomey.item.ItemAdapter
diff --git a/javadoc/com/example/househomey/SelectFragment.html b/javadoc/com/example/househomey/SelectFragment.html index a64345b4..54cb00d8 100644 --- a/javadoc/com/example/househomey/SelectFragment.html +++ b/javadoc/com/example/househomey/SelectFragment.html @@ -74,7 +74,7 @@

Class SelectFragment

java.lang.Object
androidx.fragment.app.Fragment -
com.example.househomey.SelectFragment
+
com.example.househomey.home.SelectFragment
diff --git a/javadoc/com/example/househomey/User.html b/javadoc/com/example/househomey/User.html index 5a88e9a4..8bd1d8eb 100644 --- a/javadoc/com/example/househomey/User.html +++ b/javadoc/com/example/househomey/User.html @@ -73,7 +73,7 @@

Class User

java.lang.Object -
com.example.househomey.User
+
com.example.househomey.user.User

diff --git a/javadoc/com/example/househomey/ViewItemFragment.html b/javadoc/com/example/househomey/ViewItemFragment.html index c3e5f57b..a92b9acd 100644 --- a/javadoc/com/example/househomey/ViewItemFragment.html +++ b/javadoc/com/example/househomey/ViewItemFragment.html @@ -74,7 +74,7 @@

Class ViewItemFragment

java.lang.Object
androidx.fragment.app.Fragment -
com.example.househomey.ViewItemFragment
+
com.example.househomey.item.ViewItemFragment
diff --git a/javadoc/com/example/househomey/class-use/DeleteItemsFragment.DeleteCallBack.html b/javadoc/com/example/househomey/class-use/DeleteItemsFragment.DeleteCallBack.html index ace24a73..244d46b0 100644 --- a/javadoc/com/example/househomey/class-use/DeleteItemsFragment.DeleteCallBack.html +++ b/javadoc/com/example/househomey/class-use/DeleteItemsFragment.DeleteCallBack.html @@ -2,7 +2,7 @@ -Uses of Interface com.example.househomey.DeleteItemsFragment.DeleteCallBack +Uses of Interface com.example.househomey.home.DeleteItemsFragment.DeleteCallBack @@ -49,7 +49,7 @@
-

Uses of Interface
com.example.househomey.DeleteItemsFragment.DeleteCallBack

+

Uses of Interface
com.example.househomey.home.DeleteItemsFragment.DeleteCallBack

@@ -81,7 +81,7 @@

Uses of Constructor

Description
 
-
DeleteItemsFragment(DeleteItemsFragment.DeleteCallBack deleteCallBack, +
Constructor for the Delete Items confirmation dialog fragment
diff --git a/javadoc/com/example/househomey/class-use/DeleteItemsFragment.html b/javadoc/com/example/househomey/class-use/DeleteItemsFragment.html index e5e39155..eab08fa0 100644 --- a/javadoc/com/example/househomey/class-use/DeleteItemsFragment.html +++ b/javadoc/com/example/househomey/class-use/DeleteItemsFragment.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.DeleteItemsFragment +Uses of Class com.example.househomey.home.DeleteItemsFragment @@ -49,9 +49,9 @@
-

Uses of Class
com.example.househomey.DeleteItemsFragment

+

Uses of Class
com.example.househomey.home.DeleteItemsFragment

-No usage of com.example.househomey.DeleteItemsFragment
+No usage of com.example.househomey.home.DeleteItemsFragment
diff --git a/javadoc/com/example/househomey/class-use/HomeFragment.html b/javadoc/com/example/househomey/class-use/HomeFragment.html index 128757fc..5a34d565 100644 --- a/javadoc/com/example/househomey/class-use/HomeFragment.html +++ b/javadoc/com/example/househomey/class-use/HomeFragment.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.HomeFragment +Uses of Class com.example.househomey.home.HomeFragment @@ -49,9 +49,9 @@
-

Uses of Class
com.example.househomey.HomeFragment

+

Uses of Class
com.example.househomey.home.HomeFragment

-No usage of com.example.househomey.HomeFragment
+No usage of com.example.househomey.home.HomeFragment
diff --git a/javadoc/com/example/househomey/class-use/Item.html b/javadoc/com/example/househomey/class-use/Item.html index 656c54e9..2a7189b6 100644 --- a/javadoc/com/example/househomey/class-use/Item.html +++ b/javadoc/com/example/househomey/class-use/Item.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.Item +Uses of Class com.example.househomey.item.Item @@ -49,7 +49,7 @@
-

Uses of Class
com.example.househomey.Item

+

Uses of Class
com.example.househomey.item.Item

Packages that use Item
@@ -119,7 +119,7 @@

Uses of Item<
Constructor
Description
 
-
DeleteItemsFragment(DeleteItemsFragment.DeleteCallBack deleteCallBack, +
Constructor for the Delete Items confirmation dialog fragment
@@ -218,7 +218,7 @@

Uses of Item<
Constructor
Description
 
-
+
Constructs a new EditItemFragment with the item to edit.
@@ -234,25 +234,25 @@

Uses of Item<
Method
Description
int
-
CostComparator.compare(Item o1, +
CostComparator.compare(Item o1, Item o2)
Compare 2 items from their costs
int
-
DateComparator.compare(Item o1, +
DateComparator.compare(Item o1, Item o2)
Compares 2 items by comparing their dates
int
-
DescriptionComparator.compare(Item o1, +
DescriptionComparator.compare(Item o1, Item o2)
Compares 2 items by comparing their descriptions alphabetically
int
-
MakeComparator.compare(Item o1, +
MakeComparator.compare(Item o1, Item o2)
Compares 2 Items by comparing their Makes alphabetically
diff --git a/javadoc/com/example/househomey/class-use/ItemAdapter.html b/javadoc/com/example/househomey/class-use/ItemAdapter.html index 0abbf580..5560a4f2 100644 --- a/javadoc/com/example/househomey/class-use/ItemAdapter.html +++ b/javadoc/com/example/househomey/class-use/ItemAdapter.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.ItemAdapter +Uses of Class com.example.househomey.item.ItemAdapter @@ -49,9 +49,9 @@
-

Uses of Class
com.example.househomey.ItemAdapter

+

Uses of Class
com.example.househomey.item.ItemAdapter

-No usage of com.example.househomey.ItemAdapter
+No usage of com.example.househomey.item.ItemAdapter

diff --git a/javadoc/com/example/househomey/class-use/SelectFragment.html b/javadoc/com/example/househomey/class-use/SelectFragment.html index 8bbadf20..23f1883b 100644 --- a/javadoc/com/example/househomey/class-use/SelectFragment.html +++ b/javadoc/com/example/househomey/class-use/SelectFragment.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.SelectFragment +Uses of Class com.example.househomey.home.SelectFragment @@ -49,9 +49,9 @@
-

Uses of Class
com.example.househomey.SelectFragment

+

Uses of Class
com.example.househomey.home.SelectFragment

-No usage of com.example.househomey.SelectFragment
+No usage of com.example.househomey.home.SelectFragment
diff --git a/javadoc/com/example/househomey/class-use/User.html b/javadoc/com/example/househomey/class-use/User.html index c4e86af6..7fddf72c 100644 --- a/javadoc/com/example/househomey/class-use/User.html +++ b/javadoc/com/example/househomey/class-use/User.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.User +Uses of Class com.example.househomey.user.User @@ -49,9 +49,9 @@
-

Uses of Class
com.example.househomey.User

+

Uses of Class
com.example.househomey.user.User

-No usage of com.example.househomey.User
+No usage of com.example.househomey.user.User
diff --git a/javadoc/com/example/househomey/class-use/ViewItemFragment.html b/javadoc/com/example/househomey/class-use/ViewItemFragment.html index 5ffbcbf3..6f47305b 100644 --- a/javadoc/com/example/househomey/class-use/ViewItemFragment.html +++ b/javadoc/com/example/househomey/class-use/ViewItemFragment.html @@ -2,7 +2,7 @@ -Uses of Class com.example.househomey.ViewItemFragment +Uses of Class com.example.househomey.item.ViewItemFragment @@ -49,9 +49,9 @@
-

Uses of Class
com.example.househomey.ViewItemFragment

+

Uses of Class
com.example.househomey.item.ViewItemFragment

-No usage of com.example.househomey.ViewItemFragment
+No usage of com.example.househomey.item.ViewItemFragment
diff --git a/javadoc/com/example/househomey/form/EditItemFragment.html b/javadoc/com/example/househomey/form/EditItemFragment.html index a8be290f..cd482ea6 100644 --- a/javadoc/com/example/househomey/form/EditItemFragment.html +++ b/javadoc/com/example/househomey/form/EditItemFragment.html @@ -124,7 +124,7 @@

Constructor Summary

Constructor
Description
- +
Constructs a new EditItemFragment with the item to edit.
@@ -173,7 +173,7 @@

Methods inherited from cl

Constructor Details

  • -
    +

    EditItemFragment

    public EditItemFragment(Item item)
    Constructs a new EditItemFragment with the item to edit.
    diff --git a/javadoc/com/example/househomey/sort/CostComparator.html b/javadoc/com/example/househomey/sort/CostComparator.html index a0078922..df224dca 100644 --- a/javadoc/com/example/househomey/sort/CostComparator.html +++ b/javadoc/com/example/househomey/sort/CostComparator.html @@ -117,7 +117,7 @@

    Method Summary

    Method
    Description
    int
    -
    compare(Item o1, +
    compare(Item o1, Item o2)
    Compare 2 items from their costs
    @@ -157,7 +157,7 @@

    CostComparator

    Method Details

    • -
      +

      compare

      public int compare(Item o1, Item o2)
      diff --git a/javadoc/com/example/househomey/sort/DateComparator.html b/javadoc/com/example/househomey/sort/DateComparator.html index cd55358a..54e0b3b8 100644 --- a/javadoc/com/example/househomey/sort/DateComparator.html +++ b/javadoc/com/example/househomey/sort/DateComparator.html @@ -117,7 +117,7 @@

      Method Summary

      Method
      Description
      int
      -
      compare(Item o1, +
      compare(Item o1, Item o2)
      Compares 2 items by comparing their dates
      @@ -157,7 +157,7 @@

      DateComparator

      Method Details

      • -
        +

        compare

        public int compare(Item o1, Item o2)
        diff --git a/javadoc/com/example/househomey/sort/DescriptionComparator.html b/javadoc/com/example/househomey/sort/DescriptionComparator.html index bd5d10ae..2e4c7a2a 100644 --- a/javadoc/com/example/househomey/sort/DescriptionComparator.html +++ b/javadoc/com/example/househomey/sort/DescriptionComparator.html @@ -117,7 +117,7 @@

        Method Summary

        Method
        Description
        int
        -
        compare(Item o1, +
        compare(Item o1, Item o2)
        Compares 2 items by comparing their descriptions alphabetically
        @@ -157,7 +157,7 @@

        DescriptionComparator

        Method Details