diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4925314b..c4d9d0c2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -46,6 +46,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java b/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java
index ea98e150..73a811ac 100644
--- a/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java
+++ b/app/src/main/java/com/example/petstoremobile/fragments/ProfileFragment.java
@@ -1,24 +1,252 @@
package com.example.petstoremobile.fragments;
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Bundle;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
+import android.provider.MediaStore;
+import android.text.InputType;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
import com.example.petstoremobile.R;
+import com.example.petstoremobile.activities.MainActivity;
+
+import java.io.File;
public class ProfileFragment extends Fragment {
+ //initialize the view/controls
+ private ImageView imgProfile;
+ private TextView tvProfileName, tvProfileEmail, tvProfilePhone, tvProfileRole;
+ private Button btnChangePhoto, btnEditEmail, btnEditPhone, btnLogout;
+ private Uri photoUri;
- //TODO: Create functionality for profile fragment
+ //Initialize the launchers for camera and gallery
+ private ActivityResultLauncher galleryLauncher;
+ private ActivityResultLauncher cameraLauncher;
+ private ActivityResultLauncher permissionLauncher;
+
+ //Called when the fragment is created, sets up the launchers is set profile image
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Launcher to open gallery to select profile image
+ galleryLauncher = registerForActivityResult(
+ //open gallery
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ //if the user selects an image and its not null
+ if (result.getResultCode() == Activity.RESULT_OK
+ && result.getData() != null) {
+ //get the selected image and set the image to the profile
+ Uri selectedImage = result.getData().getData();
+ imgProfile.setImageURI(selectedImage);
+ //TODO: SAVE CHANGED PHOTO TO DATABASE
+ }
+ }
+ );
+
+ // Launcher for camera to open and capture profile image
+ cameraLauncher = registerForActivityResult(
+ //open camera
+ new ActivityResultContracts.TakePicture(),
+ success -> {
+ //if a photo is taken set the image profile to it otherwise do nothing
+ if (success) {
+ //Clear the old image and set the new one
+ imgProfile.setImageURI(null);
+ imgProfile.setImageURI(photoUri);
+ //TODO: SAVE CHANGED PHOTO TO DATABASE
+ }
+ }
+ );
+
+ // Launcher to request camera permission
+ permissionLauncher = registerForActivityResult(
+ //ask user for camera permission
+ new ActivityResultContracts.RequestPermission(),
+ granted -> {
+ //if the permission is granted launch the camera
+ if (granted) {
+ launchCamera();
+ }
+ else {
+ //if the permission is denied then tell the user to grant it
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Permission Permission Required")
+ .setMessage("Please grant camera permission to use this feature")
+ .setPositiveButton("Open Settings", (dialog, which) ->{
+ //open the settings page to grant the permission when they click open settings
+ Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.fromParts("package", requireContext().getPackageName(), null));
+ startActivity(intent);
+ })
+ //close the dialog when the user clicks cancel
+ .setNegativeButton("Cancel", null)
+ .show();
+ }
+ }
+ );
+ }
+
+ //TODO: MAKE PROFILE VIEW DISPLAY PROFILE DATA FROM DATABASE
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile, container, false);
+ //get all the controls from the view
+ imgProfile = view.findViewById(R.id.imgProfile);
+ tvProfileName = view.findViewById(R.id.tvProfileName);
+ tvProfileEmail = view.findViewById(R.id.tvProfileEmail);
+ tvProfilePhone = view.findViewById(R.id.tvProfilePhone);
+ tvProfileRole = view.findViewById(R.id.tvProfileRole);
+ btnChangePhoto = view.findViewById(R.id.btnChangePhoto);
+ btnEditEmail = view.findViewById(R.id.btnEditEmail);
+ btnEditPhone = view.findViewById(R.id.btnEditPhone);
+ btnLogout = view.findViewById(R.id.btnLogout);
+
+ //Set up listeners for the buttons
+ //Change photo button
+ btnChangePhoto.setOnClickListener(v -> {
+ //Show alert dialog to user to select from gallery or camera
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Change Profile Photo")
+ //set the options for the alert dialog
+ .setItems(new String[]{"Take Photo", "Choose from Gallery"}, (dialog, which) -> {
+ if (which == 0) {
+ // Choose Camera
+ //Checks if the user has granted the camera permission already
+ if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
+ //if the permission is already granted then launch the camera
+ launchCamera();
+ } else {
+ //otherwise request the permission
+ permissionLauncher.launch(Manifest.permission.CAMERA);
+ }
+ } else {
+ // Choose Gallery
+ Intent intent = new Intent(Intent.ACTION_PICK,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+ galleryLauncher.launch(intent);
+ }
+ })
+ .show();
+ });
+
+ //Edit email button
+ //When clicked open a dialog to change email
+ btnEditEmail.setOnClickListener(v -> {
+ //Make a text field for the user to enter the new email
+ EditText input = new EditText(requireContext());
+ input.setPadding(30,30,30,30);
+ input.setText(tvProfileEmail.getText().toString());
+
+ //set input type to email
+ input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
+
+
+ //Show alert dialog to user to enter new email
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Edit Email")
+ .setView(input)
+ .setPositiveButton("Save", (dialog, which) -> {
+ String newEmail = input.getText().toString();
+ //if the new value is a valid email then set the email to the new value
+ if (android.util.Patterns.EMAIL_ADDRESS.matcher(newEmail).matches()) {
+ tvProfileEmail.setText(newEmail);
+ //TODO: UPDATE THE EMAIL IN DATABASE
+ }
+ else {
+ //tell the user to email is invalid
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Error")
+ .setMessage("Email is invalid")
+ .setPositiveButton("OK", null)
+ .show();
+ }
+ })
+ .setNegativeButton("Cancel", null)
+ .show();
+ });
+
+ //Edit phone button
+ //When clicked open a dialog to change phone
+ btnEditPhone.setOnClickListener(v -> {
+ //Make a text field for the user to enter the new email
+ EditText input = new EditText(requireContext());
+ input.setPadding(30,30,30,30);
+ input.setText(tvProfilePhone.getText().toString());
+
+ //set input type to phone number
+ input.setInputType(InputType.TYPE_CLASS_PHONE);
+
+ //add canada phone number formatting to input (XXX) XXX-XXXX
+ input.addTextChangedListener(new android.telephony.PhoneNumberFormattingTextWatcher("CA"));
+ input.setFilters(new android.text.InputFilter[]{new android.text.InputFilter.LengthFilter(14)});
+
+
+ //Show alert dialog to user to enter new phone
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Edit Phone Number")
+ .setView(input)
+ .setPositiveButton("Save", (dialog, which) -> {
+ String newPhone = input.getText().toString();
+ //if the new value is format: (XXX) XXX-XXXX then set the phone to the new value
+ if (newPhone.matches("\\(\\d{3}\\) \\d{3}-\\d{4}")) { //TODO MAKE VALIDATION CLASS INSTEAD FOR THIS
+ tvProfilePhone.setText(newPhone);
+ //TODO: UPDATE PHONE IN DATABASE
+ }
+ else {
+ //tell the user to email cannot be empty
+ new AlertDialog.Builder(requireContext())
+ .setTitle("Error")
+ .setMessage("Phone number is invalid. Format: (XXX) XXX-XXXX")
+ .setPositiveButton("OK", null)
+ .show();
+ }
+ })
+ .setNegativeButton("Cancel", null)
+ .show();
+ });
+
+ //Logout button
+ btnLogout.setOnClickListener(v -> {
+ //get the intent to the main activity and clear the back stack so the back button won't allow the user to go back to the previous screen
+ Intent intent = new Intent(getActivity(), MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+ //start the activity to go to login page and finish the current activity
+ startActivity(intent);
+ requireActivity().finish();
+ });
+
return view;
}
+
+ //Helper function create a file in the cache directory to store the photo in then launch the camera to capture the photo
+ private void launchCamera() {
+ //create a file in the cache directory to store the photo in
+ File photoFile = new File(requireContext().getCacheDir(), "profile_photo.jpg");
+ //get the uri for the file made
+ photoUri = FileProvider.getUriForFile(requireContext(), requireContext().getPackageName() + ".fileprovider", photoFile);
+ //launch the camera to capture the photo and save the photo to photoUri
+ cameraLauncher.launch(photoUri);
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/circle_image.xml b/app/src/main/res/drawable/circle_image.xml
new file mode 100644
index 00000000..8635e87b
--- /dev/null
+++ b/app/src/main/res/drawable/circle_image.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/rounded_card.xml b/app/src/main/res/drawable/rounded_card.xml
new file mode 100644
index 00000000..125ee629
--- /dev/null
+++ b/app/src/main/res/drawable/rounded_card.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml
index 6f86ca85..0342a165 100644
--- a/app/src/main/res/layout/fragment_profile.xml
+++ b/app/src/main/res/layout/fragment_profile.xml
@@ -1,88 +1,183 @@
-
+ android:background="#F5F5F5">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:orientation="vertical"
+ android:padding="24dp"
+ android:gravity="center_horizontal">
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml
new file mode 100644
index 00000000..63a0f72f
--- /dev/null
+++ b/app/src/main/res/xml/file_paths.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file