@@ -9,6 +9,7 @@ import android.app.Fragment;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -47,9 +48,12 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
|||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.ui.SimpleViewHolder;
|
import org.joinmastodon.android.ui.SimpleViewHolder;
|
||||||
|
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
|
||||||
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
import org.joinmastodon.android.ui.drawables.CoverOverlayGradientDrawable;
|
||||||
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||||
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
import org.joinmastodon.android.ui.tabs.TabLayoutMediator;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
@@ -121,6 +125,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private boolean refreshing;
|
private boolean refreshing;
|
||||||
private View fab;
|
private View fab;
|
||||||
private WindowInsets childInsets;
|
private WindowInsets childInsets;
|
||||||
|
private PhotoViewer currentPhotoViewer;
|
||||||
|
|
||||||
public ProfileFragment(){
|
public ProfileFragment(){
|
||||||
super(R.layout.loader_fragment_overlay_toolbar);
|
super(R.layout.loader_fragment_overlay_toolbar);
|
||||||
@@ -595,12 +600,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||||
int topBarsH=getToolbar().getHeight()+statusBarHeight;
|
int topBarsH=getToolbar().getHeight()+statusBarHeight;
|
||||||
if(scrollY>avatar.getTop()-topBarsH){
|
if(scrollY>avatarBorder.getTop()-topBarsH){
|
||||||
float avaAlpha=Math.max(1f-((scrollY-(avatar.getTop()-topBarsH))/(float)V.dp(38)), 0f);
|
float avaAlpha=Math.max(1f-((scrollY-(avatarBorder.getTop()-topBarsH))/(float)V.dp(38)), 0f);
|
||||||
avatar.setAlpha(avaAlpha);
|
|
||||||
avatarBorder.setAlpha(avaAlpha);
|
avatarBorder.setAlpha(avaAlpha);
|
||||||
}else{
|
}else{
|
||||||
avatar.setAlpha(1f);
|
|
||||||
avatarBorder.setAlpha(1f);
|
avatarBorder.setAlpha(1f);
|
||||||
}
|
}
|
||||||
if(scrollY>cover.getHeight()-topBarsH){
|
if(scrollY>cover.getHeight()-topBarsH){
|
||||||
@@ -622,6 +625,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
toolbarTitleView.setTranslationY(titleTransY);
|
toolbarTitleView.setTranslationY(titleTransY);
|
||||||
toolbarSubtitleView.setTranslationY(titleTransY);
|
toolbarSubtitleView.setTranslationY(titleTransY);
|
||||||
}
|
}
|
||||||
|
if(currentPhotoViewer!=null){
|
||||||
|
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
@@ -804,15 +810,38 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Attachment> createFakeAttachments(String url, Drawable drawable){
|
||||||
|
Attachment att=new Attachment();
|
||||||
|
att.type=Attachment.Type.IMAGE;
|
||||||
|
att.url=url;
|
||||||
|
att.meta=new Attachment.Metadata();
|
||||||
|
att.meta.width=drawable.getIntrinsicWidth();
|
||||||
|
att.meta.height=drawable.getIntrinsicHeight();
|
||||||
|
return Collections.singletonList(att);
|
||||||
|
}
|
||||||
|
|
||||||
private void onAvatarClick(View v){
|
private void onAvatarClick(View v){
|
||||||
if(isInEditMode){
|
if(isInEditMode){
|
||||||
startImagePicker(AVATAR_RESULT);
|
startImagePicker(AVATAR_RESULT);
|
||||||
|
}else{
|
||||||
|
Drawable ava=avatar.getDrawable();
|
||||||
|
if(ava==null)
|
||||||
|
return;
|
||||||
|
int radius=V.dp(25);
|
||||||
|
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.avatar, ava), 0,
|
||||||
|
new SingleImagePhotoViewerListener(avatar, avatarBorder, new int[]{radius, radius, radius, radius}, this, ()->currentPhotoViewer=null, ()->ava, null, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCoverClick(View v){
|
private void onCoverClick(View v){
|
||||||
if(isInEditMode){
|
if(isInEditMode){
|
||||||
startImagePicker(COVER_RESULT);
|
startImagePicker(COVER_RESULT);
|
||||||
|
}else{
|
||||||
|
Drawable drawable=cover.getDrawable();
|
||||||
|
if(drawable==null || drawable instanceof ColorDrawable)
|
||||||
|
return;
|
||||||
|
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.header, drawable), 0,
|
||||||
|
new SingleImagePhotoViewerListener(cover, cover, null, this, ()->currentPhotoViewer=null, ()->drawable, ()->avatarBorder.setTranslationZ(2), ()->avatarBorder.setTranslationZ(0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class SingleImagePhotoViewerListener implements PhotoViewer.Listener{
|
||||||
|
private final View sourceView, transformView;
|
||||||
|
private final int[] cornerRadius;
|
||||||
|
private final Runnable onDismissed;
|
||||||
|
private final Fragment parentFragment;
|
||||||
|
private final Supplier<Drawable> currentDrawableSupplier;
|
||||||
|
private final Runnable onStart, onEnd;
|
||||||
|
|
||||||
|
private float origAlpha;
|
||||||
|
|
||||||
|
public SingleImagePhotoViewerListener(View sourceView, View transformView, int[] cornerRadius, Fragment parentFragment, Runnable onDismissed, Supplier<Drawable> currentDrawableSupplier, Runnable onStart, Runnable onEnd){
|
||||||
|
this.sourceView=sourceView;
|
||||||
|
this.transformView=transformView;
|
||||||
|
this.cornerRadius=cornerRadius;
|
||||||
|
this.onDismissed=onDismissed;
|
||||||
|
this.parentFragment=parentFragment;
|
||||||
|
this.currentDrawableSupplier=currentDrawableSupplier;
|
||||||
|
this.onStart=onStart;
|
||||||
|
this.onEnd=onEnd;
|
||||||
|
if(cornerRadius!=null && cornerRadius.length!=4)
|
||||||
|
throw new IllegalArgumentException("Corner radius must be null or have length of 4");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPhotoViewVisibility(int index, boolean visible){
|
||||||
|
transformView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
|
||||||
|
int[] loc={0, 0};
|
||||||
|
sourceView.getLocationOnScreen(loc);
|
||||||
|
outRect.set(loc[0], loc[1], loc[0]+sourceView.getWidth(), loc[1]+sourceView.getHeight());
|
||||||
|
if(cornerRadius!=null)
|
||||||
|
System.arraycopy(cornerRadius, 0, outCornerRadius, 0, 4);
|
||||||
|
transformView.setTranslationZ(1);
|
||||||
|
if(onStart!=null)
|
||||||
|
onStart.run();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTransitioningViewTransform(float translateX, float translateY, float scale){
|
||||||
|
transformView.setTranslationX(translateX);
|
||||||
|
transformView.setTranslationY(translateY);
|
||||||
|
transformView.setScaleX(scale);
|
||||||
|
transformView.setScaleY(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endPhotoViewTransition(){
|
||||||
|
setTransitioningViewTransform(0f, 0f, 1f);
|
||||||
|
transformView.setTranslationZ(0);
|
||||||
|
if(onEnd!=null)
|
||||||
|
onEnd.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Drawable getPhotoViewCurrentDrawable(int index){
|
||||||
|
return currentDrawableSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void photoViewerDismissed(){
|
||||||
|
onDismissed.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissions(String[] permissions){
|
||||||
|
parentFragment.requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
@@ -54,6 +55,9 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
|||||||
private float lastFlingVelocityY;
|
private float lastFlingVelocityY;
|
||||||
private float backgroundAlphaForTransition=1f;
|
private float backgroundAlphaForTransition=1f;
|
||||||
private boolean forceUpdateLayout;
|
private boolean forceUpdateLayout;
|
||||||
|
private int[] transitionCornerRadius;
|
||||||
|
private Path transitionClipPath=new Path();
|
||||||
|
private float[] tmpFloatArray=new float[8];
|
||||||
|
|
||||||
private static final String TAG="ZoomPanView";
|
private static final String TAG="ZoomPanView";
|
||||||
|
|
||||||
@@ -148,10 +152,25 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
|||||||
child.getMatrix().mapRect(tmpRect2);
|
child.getMatrix().mapRect(tmpRect2);
|
||||||
tmpRect2.offset(child.getLeft(), child.getTop());
|
tmpRect2.offset(child.getLeft(), child.getTop());
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(interpolate(tmpRect2.left, tmpRect.left, cropAnimationValue),
|
if(transitionCornerRadius!=null){
|
||||||
interpolate(tmpRect2.top, tmpRect.top, cropAnimationValue),
|
float radiusScale=child.getScaleX();
|
||||||
interpolate(tmpRect2.right, tmpRect.right, cropAnimationValue),
|
tmpFloatArray[0]=tmpFloatArray[1]=(float)transitionCornerRadius[0]*radiusScale*(1f-cropAnimationValue);
|
||||||
interpolate(tmpRect2.bottom, tmpRect.bottom, cropAnimationValue));
|
tmpFloatArray[2]=tmpFloatArray[3]=(float)transitionCornerRadius[1]*radiusScale*(1f-cropAnimationValue);
|
||||||
|
tmpFloatArray[4]=tmpFloatArray[5]=(float)transitionCornerRadius[2]*radiusScale*(1f-cropAnimationValue);
|
||||||
|
tmpFloatArray[6]=tmpFloatArray[7]=(float)transitionCornerRadius[3]*radiusScale*(1f-cropAnimationValue);
|
||||||
|
transitionClipPath.rewind();
|
||||||
|
transitionClipPath.addRoundRect(interpolate(tmpRect2.left, tmpRect.left, cropAnimationValue),
|
||||||
|
interpolate(tmpRect2.top, tmpRect.top, cropAnimationValue),
|
||||||
|
interpolate(tmpRect2.right, tmpRect.right, cropAnimationValue),
|
||||||
|
interpolate(tmpRect2.bottom, tmpRect.bottom, cropAnimationValue),
|
||||||
|
tmpFloatArray, Path.Direction.CW);
|
||||||
|
canvas.clipPath(transitionClipPath);
|
||||||
|
}else{
|
||||||
|
canvas.clipRect(interpolate(tmpRect2.left, tmpRect.left, cropAnimationValue),
|
||||||
|
interpolate(tmpRect2.top, tmpRect.top, cropAnimationValue),
|
||||||
|
interpolate(tmpRect2.right, tmpRect.right, cropAnimationValue),
|
||||||
|
interpolate(tmpRect2.bottom, tmpRect.bottom, cropAnimationValue));
|
||||||
|
}
|
||||||
boolean res=super.drawChild(canvas, child, drawingTime);
|
boolean res=super.drawChild(canvas, child, drawingTime);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
return res;
|
return res;
|
||||||
@@ -189,6 +208,18 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
|||||||
return initialScale;
|
return initialScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateAndSetCornerRadius(int[] cornerRadius){
|
||||||
|
transitionCornerRadius=null;
|
||||||
|
if(cornerRadius!=null && cornerRadius.length==4){
|
||||||
|
for(int corner:cornerRadius){
|
||||||
|
if(corner>0){
|
||||||
|
transitionCornerRadius=cornerRadius;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void animateIn(Rect rect, int[] cornerRadius){
|
public void animateIn(Rect rect, int[] cornerRadius){
|
||||||
int[] loc={0, 0};
|
int[] loc={0, 0};
|
||||||
getLocationOnScreen(loc);
|
getLocationOnScreen(loc);
|
||||||
@@ -204,6 +235,7 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
|||||||
animatingTransition=true;
|
animatingTransition=true;
|
||||||
|
|
||||||
matrix.getValues(matrixValues);
|
matrix.getValues(matrixValues);
|
||||||
|
validateAndSetCornerRadius(cornerRadius);
|
||||||
|
|
||||||
child.setAlpha(0f);
|
child.setAlpha(0f);
|
||||||
setupAndStartTransitionAnim(new SpringAnimation(this, CROP_AND_FADE, 1f).setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE));
|
setupAndStartTransitionAnim(new SpringAnimation(this, CROP_AND_FADE, 1f).setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE));
|
||||||
@@ -233,6 +265,7 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
|||||||
animatingTransition=true;
|
animatingTransition=true;
|
||||||
dismissAfterTransition=true;
|
dismissAfterTransition=true;
|
||||||
rawCropAndFadeValue=1f;
|
rawCropAndFadeValue=1f;
|
||||||
|
validateAndSetCornerRadius(cornerRadius);
|
||||||
|
|
||||||
setupAndStartTransitionAnim(new SpringAnimation(this, CROP_AND_FADE, 0f).setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE));
|
setupAndStartTransitionAnim(new SpringAnimation(this, CROP_AND_FADE, 0f).setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE));
|
||||||
setupAndStartTransitionAnim(new SpringAnimation(child, DynamicAnimation.SCALE_X, initialScale));
|
setupAndStartTransitionAnim(new SpringAnimation(child, DynamicAnimation.SCALE_X, initialScale));
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
tools:visibility="visible"
|
tools:visibility="visible"
|
||||||
android:text="@string/follows_you"/>
|
android:text="@string/follows_you"/>
|
||||||
|
|
||||||
<View
|
<FrameLayout
|
||||||
android:id="@+id/avatar_border"
|
android:id="@+id/avatar_border"
|
||||||
android:layout_width="102dp"
|
android:layout_width="102dp"
|
||||||
android:layout_height="102dp"
|
android:layout_height="102dp"
|
||||||
@@ -60,19 +60,19 @@
|
|||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginTop="-40dp"
|
android:layout_marginTop="-40dp"
|
||||||
android:layout_marginStart="14dp"
|
android:layout_marginStart="14dp"
|
||||||
android:background="@drawable/profile_ava_bg"/>
|
android:outlineProvider="@null"
|
||||||
|
android:background="@drawable/profile_ava_bg">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
android:layout_width="98dp"
|
android:layout_width="98dp"
|
||||||
android:layout_height="98dp"
|
android:layout_height="98dp"
|
||||||
android:layout_below="@id/cover"
|
android:layout_gravity="center"
|
||||||
android:layout_alignParentStart="true"
|
android:scaleType="centerCrop"
|
||||||
android:layout_marginStart="16dp"
|
android:contentDescription="@string/profile_picture"
|
||||||
android:layout_marginTop="-38dp"
|
tools:src="#0f0" />
|
||||||
android:scaleType="centerCrop"
|
|
||||||
android:contentDescription="@string/profile_picture"
|
</FrameLayout>
|
||||||
tools:src="#0f0" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/profile_counters"
|
android:id="@+id/profile_counters"
|
||||||
@@ -196,10 +196,10 @@
|
|||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/avatar"
|
android:layout_below="@id/avatar_border"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
||||||
android:textAppearance="@style/m3_headline_small"
|
android:textAppearance="@style/m3_headline_small"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
@@ -232,10 +232,10 @@
|
|||||||
android:id="@+id/name_edit"
|
android:id="@+id/name_edit"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/avatar"
|
android:layout_below="@id/avatar_border"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
android:layout_toStartOf="@id/profile_action_btn_wrap"
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:textAppearance="@style/m3_body_large"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
|
|||||||
Reference in New Issue
Block a user