Skip to content
Open
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
175 changes: 172 additions & 3 deletions app/src/main/java/fi/bitrite/android/ws/ui/UserFragment.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package fi.bitrite.android.ws.ui;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
Expand All @@ -15,14 +22,18 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
Expand All @@ -38,9 +49,11 @@
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindColor;
import butterknife.BindInt;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;
import butterknife.OnClick;
import fi.bitrite.android.ws.R;
import fi.bitrite.android.ws.api.helper.HttpErrorHelper;
import fi.bitrite.android.ws.model.Feedback;
Expand Down Expand Up @@ -76,6 +89,7 @@ public class UserFragment extends BaseFragment {
@BindView(R.id.user_swipe_refresh) SwipeRefreshLayout mSwipeRefresh;
@BindView(R.id.user_layout_details) LinearLayout mLayoutDetails;
@BindView(R.id.user_img_photo) ImageView mImgPhoto;
@BindView(R.id.user_img_expanded) ImageView mImgExpanded;
@BindView(R.id.user_lbl_name) TextView mLblName;

@BindView(R.id.user_img_favorite) ImageView mImgFavorite;
Expand All @@ -101,6 +115,8 @@ public class UserFragment extends BaseFragment {
@BindColor(R.color.rating_neutral) int mRatingColorNeutral;
@BindColor(R.color.rating_negative) int mRatingColorNegative;

@BindInt(android.R.integer.config_shortAnimTime) int mShortAnimationDuration;

private BehaviorSubject<UserInfoLoadResult> mLastUserInfoLoadResult = BehaviorSubject.create();

private int mUserId;
Expand All @@ -109,6 +125,9 @@ public class UserFragment extends BaseFragment {
private final BehaviorSubject<List<Feedback>> mFeedbacks = BehaviorSubject.create();
private final BehaviorSubject<Boolean> mFavorite = BehaviorSubject.create();

private boolean mIsImgExpandible;
private Animator mCurrentAnimator;

private boolean mDbFavoriteStatus;
private FeedbackListAdapter mFeedbackListAdapter;

Expand Down Expand Up @@ -319,12 +338,34 @@ private void updateUserViewContent() {
}

// If we're connected and there is a picture, get user picture.
mIsImgExpandible = false;
mImgPhoto.setImageResource(R.drawable.default_userinfo_profile);
String url = user.profilePicture.getLargeUrl();
if (!TextUtils.isEmpty(url)) {
WSGlide.with(requireContext())
.load(url)
.apply(new RequestOptions().placeholder(R.drawable.default_userinfo_profile))
.into(mImgPhoto);
.addListener(new RequestListener<Drawable>() {
@Override
public boolean onResourceReady(Drawable resource,
Object model,
Target<Drawable> target,
DataSource dataSource,
boolean isFirstResource) {
mIsImgExpandible = true;
mImgPhoto.setImageDrawable(resource);
mImgExpanded.setImageDrawable(resource);
return false;
}

@Override
public boolean onLoadFailed(@Nullable GlideException e,
Object model,
Target<Drawable> target,
boolean isFirstResource) {
return false;
}
})
.preload();
mImgPhoto.setContentDescription(
getString(R.string.content_description_avatar_of_var, user.getName()));
}
Expand Down Expand Up @@ -403,6 +444,134 @@ private void loadUserInformation() {
.subscribe();
}

@OnClick(R.id.user_img_photo)
void zoomImageFromThumb() {
if (!mIsImgExpandible) {
return;
}

// If there's an animation in progress, cancel it
// immediately and proceed with this one.
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}

// Load the high-resolution "zoomed-in" image.
mImgExpanded.setImageDrawable(mImgPhoto.getDrawable());

// Calculate the starting and ending bounds for the zoomed-in image.
// This step involves lots of math. Yay, math.
final Rect startBounds = new Rect();
final Rect finalBounds = new Rect();
final Point globalOffset = new Point();

// The start bounds are the global visible rectangle of the thumbnail,
// and the final bounds are the global visible rectangle of the container
// view. Also set the container view's offset as the origin for the
// bounds, since that's the origin for the positioning animation
// properties (X, Y).
mImgPhoto.getGlobalVisibleRect(startBounds);
mSwipeRefresh.getGlobalVisibleRect(finalBounds, globalOffset);
startBounds.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);

// Adjust the start bounds to be the same aspect ratio as the final
// bounds using the "center crop" technique. This prevents undesirable
// stretching during the animation. Also calculate the start scaling
// factor (the end scaling factor is always 1.0).
float startScale;
if ((float) finalBounds.width() / finalBounds.height()
> (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
startScale = (float) startBounds.height() / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
} else {
// Extend start bounds vertically
startScale = (float) startBounds.width() / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
}

// Hide the thumbnail and show the zoomed-in view. When the animation
// begins, it will position the zoomed-in view in the place of the
// thumbnail.
mImgPhoto.setAlpha(0.3f);
mImgExpanded.setVisibility(View.VISIBLE);

// Set the pivot point for SCALE_X and SCALE_Y transformations
// to the top-left corner of the zoomed-in view (the default
// is the center of the view).
mImgExpanded.setPivotX(0f);
mImgExpanded.setPivotY(0f);

// Construct and run the parallel animation of the four translation and
// scale properties (X, Y, SCALE_X, and SCALE_Y).
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(mImgExpanded, View.X, startBounds.left, finalBounds.left))
.with(ObjectAnimator.ofFloat(mImgExpanded, View.Y, startBounds.top, finalBounds.top))
.with(ObjectAnimator.ofFloat(mImgExpanded, View.SCALE_X, startScale, 1f))
.with(ObjectAnimator.ofFloat(mImgExpanded, View.SCALE_Y, startScale, 1f));
set.setDuration(mShortAnimationDuration);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
}

@Override
public void onAnimationCancel(Animator animation) {
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;

// Upon clicking the zoomed-in image, it should zoom back down
// to the original bounds and show the thumbnail instead of
// the expanded image.
final float startScaleFinal = startScale;
mImgExpanded.setOnClickListener(view -> {
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}

// Animate the four positioning/sizing properties in parallel,
// back to their original values.
AnimatorSet set1 = new AnimatorSet();
set1
.play(ObjectAnimator.ofFloat(mImgExpanded, View.X, startBounds.left))
.with(ObjectAnimator.ofFloat(mImgExpanded, View.Y,startBounds.top))
.with(ObjectAnimator.ofFloat(mImgExpanded, View.SCALE_X, startScaleFinal))
.with(ObjectAnimator.ofFloat(mImgExpanded, View.SCALE_Y, startScaleFinal));
set1.setDuration(mShortAnimationDuration);
set1.setInterpolator(new DecelerateInterpolator());
set1.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mImgPhoto.setAlpha(1f);
mImgExpanded.setVisibility(View.GONE);
mCurrentAnimator = null;
}

@Override
public void onAnimationCancel(Animator animation) {
mImgPhoto.setAlpha(1f);
mImgExpanded.setVisibility(View.GONE);
mCurrentAnimator = null;
}
});
set1.start();
mCurrentAnimator = set1;
});
}

private void reloadUserInformation() {
mUserRepository.markAsOld(mUserId);
mFeedbackRepository.markAsOldForRecipient(mUserId);
Expand Down
Loading