Skip to content
This repository has been archived by the owner on Feb 1, 2025. It is now read-only.

Implement editable profile picture on profile page #36

Merged
merged 5 commits into from
Mar 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void testEditButton() {
// Test whether the profile picture is displayed
@Test
public void testProfilePicture() {
onView(withId(R.id.profile_img)).check(matches(isDisplayed()));
onView(withId(R.id.profile_image)).check(matches(isDisplayed()));
}

// Test whether the username text is displayed and contains the correct text
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,29 @@
package com.github.sdp.mediato;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Toast;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.github.dhaval2404.imagepicker.ImagePicker;
import com.github.javafaker.Faker;
import com.github.sdp.mediato.utility.PhotoPicker;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;

import java.util.regex.Matcher;

/**
* create an instance of this fragment.
*/
public class CreateProfileFragment extends Fragment {

private ImageView profileImage;
private Uri profileImageUri;
private PhotoPicker photoPicker;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Expand All @@ -48,9 +35,12 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
final FloatingActionButton profileImageButton = view.findViewById(R.id.profile_image_add_button);

profileImage = view.findViewById(R.id.profile_image);
photoPicker = new PhotoPicker(this, profileImage);

// Open a photo picker to choose the profile image
profileImageButton.setOnClickListener(photoPicker());
profileImageButton.setOnClickListener(v ->
photoPicker.getOnClickListener(requireActivity().getActivityResultRegistry()).onClick(v)
);

// Generate a username
usernameTextInput.setEndIconOnClickListener(generateUsername(usernameTextInput, usernameEditText));
Expand All @@ -65,41 +55,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
return view;
}

@NonNull
private View.OnClickListener photoPicker() {
return v -> ImagePicker.Companion.with(CreateProfileFragment.this)
.crop()
.cropSquare()
.compress(1024)
.maxResultSize(620, 620)
.createIntent( intent -> {
photoPickerResult.launch(intent);
return null;
});
}

// Get the result from the photoPicker()
private final ActivityResultLauncher<Intent> photoPickerResult = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
int resultCode = result.getResultCode();
Intent data = result.getData();

if (resultCode == Activity.RESULT_OK) {
if (data != null) {
profileImageUri = data.getData();
profileImage.setImageURI(profileImageUri);
}
} else if (resultCode == ImagePicker.RESULT_ERROR){
Toast.makeText(getActivity(), ImagePicker.getError(data), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), "Task Cancelled", Toast.LENGTH_SHORT).show();
}
}
});

@NonNull
private View.OnClickListener generateUsername(TextInputLayout usernameTextInput, TextInputEditText usernameEditText) {
return v -> {
Expand Down
37 changes: 30 additions & 7 deletions app/src/main/java/com/github/sdp/mediato/ProfileFragment.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
package com.github.sdp.mediato;

import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;

import androidx.fragment.app.Fragment;

import com.github.sdp.mediato.utility.PhotoPicker;

/**
* A fragment that displays the user's profile information, including their profile picture,
* username, and collections of their favorite media types. The profile picture and collections can be edited by the user.
*/
public class ProfileFragment extends Fragment {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_profile, container, false);
}
private ImageView profileImage;
private PhotoPicker photoPicker;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_profile, container, false);

final Button edit_button = view.findViewById(R.id.edit_button);
profileImage = view.findViewById(R.id.profile_image);

// On click on the edit button, open a photo picker to choose the profile image
photoPicker = new PhotoPicker(this, profileImage);
edit_button.setOnClickListener(v ->
photoPicker.getOnClickListener(requireActivity().getActivityResultRegistry()).onClick(v)
);

return view;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,5 @@ public static void checkNullOrEmptyString(String string, String variable){
throw new IllegalArgumentException(variable + "must not be null or empty");
}
}

}
78 changes: 78 additions & 0 deletions app/src/main/java/com/github/sdp/mediato/utility/PhotoPicker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.github.sdp.mediato.utility;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.ActivityResultRegistry;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import com.github.dhaval2404.imagepicker.ImagePicker;
import com.github.sdp.mediato.R;
import com.github.sdp.mediato.errorCheck.Preconditions;

/**
* A utility class for handling photo picker functionality, including launching an activity that
* lets the user choose a picture from the gallery or take a picture directly using the camera,
* and handling the result of that activity.
*/
public class PhotoPicker {

private final Fragment fragment;
private final ImageView imageView;
private Uri profileImageUri;

public PhotoPicker(@NonNull Fragment fragment, @NonNull ImageView imageView) {
this.fragment = fragment;
this.imageView = imageView;
}

/**
* Returns a click listener that launches a photo picker and handles the result.
*
* @param activityResultRegistry the activity result registry to use for launching the photo picker
* @return a click listener that launches a photo picker and handles the result
*/
public View.OnClickListener getOnClickListener(ActivityResultRegistry activityResultRegistry) {
ActivityResultLauncher<Intent> photoPickerResult = registerForPhotoPickerResult(activityResultRegistry);
return v -> ImagePicker.Companion.with(fragment)
.crop()
.cropSquare()
.compress(1024)
.maxResultSize(imageView.getWidth(), imageView.getHeight())
.createIntent(intent -> {
photoPickerResult.launch(intent);
return null;
});
}

private ActivityResultLauncher<Intent> registerForPhotoPickerResult(ActivityResultRegistry activityResultRegistry) {
return activityResultRegistry.register("photoPickerResult", new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
handlePhotoPickerResult(result.getResultCode(), result.getData());
}
});
}

private void handlePhotoPickerResult(int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && data != null) {
profileImageUri = data.getData();
imageView.setImageURI(profileImageUri);
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(fragment.getContext(), ImagePicker.getError(data), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(fragment.getContext(), "Task Cancelled", Toast.LENGTH_SHORT).show();
}
}

}
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/profile_picture_default_white.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!-- TODO: This copy of profile_picture_default is just a temporary fix because overriding the color does not work -->
<vector android:height="72dp" android:tint="@color/white"
android:viewportHeight="24" android:viewportWidth="24"
android:width="72dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12c0,5.52 4.47,10 9.99,10C17.52,22 22,17.52 22,12C22,6.48 17.52,2 11.99,2zM8.5,8C9.33,8 10,8.67 10,9.5S9.33,11 8.5,11S7,10.33 7,9.5S7.67,8 8.5,8zM12,18c-2.28,0 -4.22,-1.66 -5,-4h10C16.22,16.34 14.28,18 12,18zM15.5,11c-0.83,0 -1.5,-0.67 -1.5,-1.5S14.67,8 15.5,8S17,8.67 17,9.5S16.33,11 15.5,11z"/>
</vector>
23 changes: 12 additions & 11 deletions app/src/main/res/layout/fragment_create_profile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,18 @@
android:focusable="true" />

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image"
android:layout_width="156dp"
android:layout_height="156dp"
android:src="@drawable/profile_picture_default"
app:civ_border_color="@color/purple_500"
app:civ_border_width="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

android:id="@+id/profile_image"
android:layout_width="@dimen/profile_picture_big"
android:layout_height="@dimen/profile_picture_big"
android:src="@drawable/profile_picture_default"
app:civ_border_color="@color/purple_500"
app:civ_border_width="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />


</androidx.constraintlayout.widget.ConstraintLayout>
Expand Down
61 changes: 26 additions & 35 deletions app/src/main/res/layout/layout_profile_header.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/profile_header"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/profile_header"
android:layout_width="match_parent"
android:layout_height="160dp"
android:background="@color/colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
app:layout_constraintVertical_bias="0.0">

<Button
android:id="@+id/friends_button"
Expand All @@ -19,42 +20,19 @@
android:text="@string/friends"
android:textAllCaps="false"
android:textColor="@color/gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/profile_img"
app:layout_constraintTop_toBottomOf="@+id/username_text"
app:layout_constraintVertical_bias="0.361" />

app:layout_constraintBottom_toBottomOf="@+id/profile_image"
app:layout_constraintStart_toEndOf="@+id/profile_image" />
<Button
android:id="@+id/edit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:layout_marginStart="30dp"
android:backgroundTint="@color/white"
android:text="@string/edit"
android:textAllCaps="false"
android:textColor="@color/gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/friends_button"
app:layout_constraintTop_toBottomOf="@+id/username_text"
app:layout_constraintVertical_bias="1.0" />

<ImageView
android:id="@+id/profile_img"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:layout_marginStart="30dp"
android:background="@drawable/bg_circle"
android:src="@drawable/ic_profile"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:tint="@color/gray" />
app:layout_constraintBottom_toBottomOf="@+id/profile_image"
app:layout_constraintStart_toEndOf="@+id/friends_button" />

<TextView
android:id="@+id/username_text"
Expand All @@ -64,9 +42,22 @@
android:text="@string/username"
android:textColor="@color/white"
android:textSize="26dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/profile_img"
app:layout_constraintTop_toTopOf="@+id/profile_img" />
app:layout_constraintStart_toEndOf="@+id/profile_image"
app:layout_constraintTop_toTopOf="@+id/profile_image" />

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_image"
android:layout_width="@dimen/profile_picture_small"
android:layout_height="@dimen/profile_picture_small"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:layout_marginStart="30dp"
android:src="@drawable/profile_picture_default_white"
app:civ_border_color="@color/white"
app:civ_border_width="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />

</androidx.constraintlayout.widget.ConstraintLayout>
5 changes: 5 additions & 0 deletions app/src/main/res/values/dimensions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="profile_picture_big">156dp</dimen>
<dimen name="profile_picture_small">100dp</dimen>
</resources>