app/src/main/java/ch/epfl/sweng/zuluzulu/fragments/ProfileFragment.java
package ch.epfl.sweng.zuluzulu.fragments;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.google.firebase.storage.FirebaseStorage;
import com.google.firebase.storage.StorageReference;
import com.google.firebase.storage.UploadTask;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import ch.epfl.sweng.zuluzulu.CommunicationTag;
import ch.epfl.sweng.zuluzulu.fragments.superFragments.FragmentWithUser;
import ch.epfl.sweng.zuluzulu.idlingResource.IdlingResourceFactory;
import ch.epfl.sweng.zuluzulu.OnFragmentInteractionListener;
import ch.epfl.sweng.zuluzulu.R;
import ch.epfl.sweng.zuluzulu.structure.user.AuthenticatedUser;
import ch.epfl.sweng.zuluzulu.structure.user.UserRole;
import ch.epfl.sweng.zuluzulu.Utility.BitmapUtils;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link ProfileFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class ProfileFragment extends FragmentWithUser<AuthenticatedUser> {
private static final String OWNER_TAG = "OWNER_TAG";
private static final int CAMERA_CODE = 1234;
private static final int W_STORAGE_PERM_CODE = 260;
private static final int ANGLE_ROTATED_90 = 90;
private static final int ANGLE_ROTATED_180 = 180;
private static final int ANGLE_ROTATED_270 = 270;
private int internal = 0;
private boolean profileOwner;
private ImageButton pic;
private String pathToImage;
private String pathToTemp;
private StorageReference pictureRef;
public ProfileFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param user User data
* @return A new instance of fragment ProfileFragment.
*/
public static ProfileFragment newInstance(AuthenticatedUser user, boolean profileOwner) {
if(user == null)
throw new IllegalArgumentException("user can't be null");
Bundle bundle = new Bundle();
bundle.putBoolean(OWNER_TAG, profileOwner);
bundle.putSerializable(ARG_USER, user);
// Transmit data
ProfileFragment profileFragment = new ProfileFragment();
profileFragment.setArguments(bundle);
return profileFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
profileOwner = (Boolean) getArguments().get(OWNER_TAG);
mListener.onFragmentInteraction(CommunicationTag.SET_TITLE, "Profil de " + user.getFirstNames());
FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageRef = storage.getReference();
pictureRef = storageRef.child("images/" + user.getSciper() + ".jpg");
} else {
throw new AssertionError("No argument");
}
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_profile, container, false);
TextView user_view = view.findViewById(R.id.profile_name_text);
StringBuilder builder = new StringBuilder();
if (user.getFirstNames() != null && user.getFirstNames().length() > 1) {
builder.append(user.getFirstNames().substring(0, 1).toUpperCase());
builder.append(user.getFirstNames().substring(1));
}
if (user.getLastNames() != null && user.getLastNames().length() > 1) {
builder.append(" ").append(user.getLastNames().substring(0, 1).toUpperCase());
builder.append(user.getLastNames().substring(1));
}
if (user.getRoles().contains(UserRole.ADMIN.toString())) {
builder.append(" - ADMIN");
}
String username = builder.toString();
user_view.setText(username);
pic = view.findViewById(R.id.profile_image);
View add_picture_view = view.findViewById(R.id.profile_add_photo);
if (profileOwner) {
add_picture_view.setOnClickListener(v -> {
if (askPermissions()) {
goToCamera();
}
});
} else {
add_picture_view.setVisibility(View.GONE);
}
if (askPermissions()) {
setBitmapFromStorage();
}
TextView gasparView = view.findViewById(R.id.profile_gaspar_text);
gasparView.setText(user.getGaspar());
TextView emailView = view.findViewById(R.id.profile_email_edit);
emailView.setText(user.getEmail());
TextView sciperView = view.findViewById(R.id.profile_sciper_edit);
sciperView.setText(user.getSciper());
TextView unit = view.findViewById(R.id.profile_unit_edit);
unit.setText(new StringBuilder().append(user.getSection()).append("-").append(user.getSemester()));
return view;
}
/**
* ask permissions for the different resources we use
*
* @return if the permissions are granted
*/
private boolean askPermissions() {
return (hasPermission()) || (internal++ < 3 && askPermissions());
}
/**
* Check if user has permission
* @return boolean
*/
private boolean hasPermission(){
boolean storage_write = ContextCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
boolean storage_read = ContextCompat.checkSelfPermission(getActivity(), android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
boolean camera = ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
if (!storage_write) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, W_STORAGE_PERM_CODE);
} else if (!storage_read) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, W_STORAGE_PERM_CODE+1);
} else if (!camera) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, W_STORAGE_PERM_CODE+2);
}
return storage_read && storage_write && camera;
}
/**
* download the file from firebase and set it into the imagebutton
*/
private void setBitmapFromStorage() {
File localFile = null;
try {
localFile = File.createTempFile("images", "jpg");
} catch (IOException e) {
Log.e("creating temp file", "unable to create a temporary file for intent");
}
if (localFile != null) {
pathToTemp = localFile.getAbsolutePath();
IdlingResourceFactory.incrementCountingIdlingResource();
pictureRef.getFile(localFile).addOnSuccessListener(taskSnapshot -> {
IdlingResourceFactory.decrementCountingIdlingResource();
// Local temp file has been created
setRescaledImage(pathToTemp);
}).addOnFailureListener(exception -> {
IdlingResourceFactory.decrementCountingIdlingResource();
// Handle any errors
Toast.makeText(getActivity(), "Unsuccessful load", Toast.LENGTH_SHORT).show();
});
}
}
/**
* create the file in which we will put the image
* and save the path to the file into pathToImage
*
* @return the file
* @throws IOException if creation fails
*/
private File createFileForPicture(){
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = new File (directory + "/user" + user.getSciper() + ".jpg");
pathToImage = image.getAbsolutePath();
return image;
}
/**
* generates an intent for the camera with the right uri and file
* so that the camera saves the file in the SD and on the firebaseStorage
*
* adapted from : https://developer.android.com/training/camera/photobasics
*/
private void goToCamera() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(Objects.requireNonNull(getActivity()).getPackageManager()) != null) {
File picture;
picture = createFileForPicture();
if (picture != null) {
Uri uri = FileProvider.getUriForFile(getActivity(), "ch.epfl.sweng.zuluzulu.fileprovider", picture);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, CAMERA_CODE);
}
}
}
/**
* receives the result of the camera activity, set the picture, and upload it to the storage
*
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_CODE && resultCode == Activity.RESULT_OK) {
Bitmap correctImage = setRescaledImage(pathToImage);
BitmapUtils.writeBitmapInSDCard(correctImage,pathToImage);
Uri uriFile = Uri.fromFile(new File(pathToImage));
UploadTask uploadTask = pictureRef.putFile(uriFile);
IdlingResourceFactory.incrementCountingIdlingResource();
uploadTask.addOnFailureListener(exception -> {
IdlingResourceFactory.decrementCountingIdlingResource();
Toast.makeText(getActivity(), "Unsuccessful upload", Toast.LENGTH_SHORT).show();
}).addOnSuccessListener(taskSnapshot -> {
IdlingResourceFactory.decrementCountingIdlingResource();
Toast.makeText(getActivity(), "Successful upload", Toast.LENGTH_SHORT).show();
});
}
}
/**
* helper method that takes a path to a file and scale it to make it fit inside the imagebutton
*
* @param path the path to the file to rescale
*
* adapted from : https://developer.android.com/training/camera/photobasics
*/
private Bitmap setRescaledImage(String path) {
int targetHeight = pic.getHeight();
if(targetHeight == 0){
targetHeight = 50;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int currentHeight = options.outHeight;
int scaling = currentHeight / targetHeight;
options.inJustDecodeBounds = false;
options.inSampleSize = scaling;
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
bitmap = rotateImageDependingOnPhoneModel(bitmap, pathToImage);
if (options.outHeight != 0) {
pic.setImageBitmap(bitmap);
}
return bitmap;
}
/**
* helper method that checks the angle the picture as been rotated and put it back straight
* @param bitmap the possibly rotated picture
* @param path the path to the picture
* @return the bitmap with the good orientation
*
* adapted from : https://stackoverflow.com/questions/14066038/why-does-an-image-captured-using-camera-intent-gets-rotated-on-some-devices-on-a
*/
private Bitmap rotateImageDependingOnPhoneModel(Bitmap bitmap , String path){
ExifInterface exifInterface;
try{
exifInterface = new ExifInterface(path);
}catch (Exception e){
return bitmap;
}
int angleToRotate = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
Bitmap correctBitmap;
switch(angleToRotate) {
case ExifInterface.ORIENTATION_ROTATE_90:
correctBitmap = BitmapUtils.rotateBitmap(bitmap, ANGLE_ROTATED_90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
correctBitmap = BitmapUtils.rotateBitmap(bitmap, ANGLE_ROTATED_180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
correctBitmap = BitmapUtils.rotateBitmap(bitmap, ANGLE_ROTATED_270);
break;
case ExifInterface.ORIENTATION_NORMAL:
default:
correctBitmap = bitmap;
}
return correctBitmap;
}
}