Allow returning to previous scroll position in home timeline (AND-189)

This commit is contained in:
Grishka 2024-10-27 05:38:15 +03:00
parent ced5fe4ee0
commit 2ad50cd972
3 changed files with 85 additions and 6 deletions

View File

@ -9,7 +9,9 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
@ -58,6 +60,7 @@ import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.sheets.DonationSheet;
import org.joinmastodon.android.ui.sheets.DonationSuccessfulSheet;
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.viewcontrollers.HomeTimelineMenuController;
import org.joinmastodon.android.ui.viewcontrollers.ToolbarDropdownMenuController;
import org.joinmastodon.android.ui.views.FixedAspectRatioImageView;
@ -106,6 +109,11 @@ public class HomeTimelineFragment extends StatusListFragment implements ToolbarD
private DiscoverInfoBannerHelper localTimelineBannerHelper;
private View donationBanner;
private boolean donationBannerDismissing;
private NestedRecyclerScrollView scrollWrapper;
private String scrollBackItemID;
private int scrollBackItemOffset, scrollBackItemIndex;
private long scrollBackTime;
private String maxID;
private String lastSavedMarkerID;
@ -323,6 +331,7 @@ public class HomeTimelineFragment extends StatusListFragment implements ToolbarD
return true;
}
});
scrollWrapper=scroller;
if(GithubSelfUpdater.needSelfUpdating()){
updateUpdateState(GithubSelfUpdater.getInstance().getState());
@ -728,7 +737,7 @@ public class HomeTimelineFragment extends StatusListFragment implements ToolbarD
private void onNewPostsBtnClick(View v){
if(newPostsBtnShown){
hideNewPostsButton();
scrollToTop();
smoothScrollRecyclerViewToTop(list);
}
}
@ -835,6 +844,67 @@ public class HomeTimelineFragment extends StatusListFragment implements ToolbarD
}
}
@Override
public void scrollToTop(){
if(list.getChildCount()==0)
return;
scrollWrapper.smoothScrollTo(0, 0);
View topChild=list.getLayoutManager().getChildAt(0);
if(list.getChildAdapterPosition(topChild)==0){
if(topChild.getTop()==list.getPaddingTop() && scrollBackItemID!=null && System.currentTimeMillis()-scrollBackTime<5*60_000){
int indexWithinPost=0;
for(int i=0;i<displayItems.size();i++){
StatusDisplayItem item=displayItems.get(i);
if(item.parentID.equals(scrollBackItemID)){
if(indexWithinPost==scrollBackItemIndex){
((LinearLayoutManager)list.getLayoutManager()).scrollToPositionWithOffset(i+getMainAdapterOffset(), scrollBackItemOffset);
list.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw(){
list.getViewTreeObserver().removeOnPreDrawListener(this);
list.scrollBy(0, V.dp(-300));
list.smoothScrollBy(0, V.dp(300));
return true;
}
});
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.S)
UiUtils.playVibrationEffectIfSupported(getActivity(), VibrationEffect.Composition.PRIMITIVE_THUD);
return;
}
indexWithinPost++;
}
}
}else{
smoothScrollRecyclerViewToTop(list);
}
}else if(list.getChildViewHolder(topChild) instanceof StatusDisplayItem.Holder<?> itemHolder){
int postIndex;
String id=itemHolder.getItemID();
for(postIndex=0;postIndex<data.size();postIndex++){
if(data.get(postIndex).id.equals(id))
break;
}
if(postIndex>1){
scrollBackItemID=id;
scrollBackItemIndex=0;
for(StatusDisplayItem item:displayItems){
if(item.parentID.equals(id)){
if(item==itemHolder.getItem())
break;
scrollBackItemIndex++;
}
}
scrollBackItemOffset=topChild.getTop();
scrollBackTime=System.currentTimeMillis();
}else{
scrollBackItemID=null;
}
}
smoothScrollRecyclerViewToTop(list);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.S)
UiUtils.playVibrationEffectIfSupported(getActivity(), VibrationEffect.Composition.PRIMITIVE_QUICK_RISE);
}
private String getCurrentListTitle(){
return switch(listMode){
case FOLLOWING -> getString(R.string.timeline_following);

View File

@ -1,6 +1,7 @@
package org.joinmastodon.android.ui.utils;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.UiModeManager;
import android.content.ActivityNotFoundException;
@ -25,6 +26,8 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.ext.SdkExtensions;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
@ -1106,4 +1109,14 @@ public class UiUtils{
return ColorContrastMode.DEFAULT;
return ColorContrastMode.fromContrastValue(context.getSystemService(UiModeManager.class).getContrast());
}
@TargetApi(Build.VERSION_CODES.R)
public static boolean playVibrationEffectIfSupported(Context context, int effect){
Vibrator vibrator=context.getSystemService(Vibrator.class);
if(vibrator.areAllPrimitivesSupported(effect)){
vibrator.vibrate(VibrationEffect.startComposition().addPrimitive(effect).compose());
return true;
}
return false;
}
}

View File

@ -350,11 +350,7 @@ public class ComposePollViewController{
pollOptionsView.removeView(view);
addPollOptionBtn.setEnabled(pollOptions.size()<maxPollOptions);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.R){
Vibrator vibrator=fragment.getActivity().getSystemService(Vibrator.class);
if(vibrator.areAllPrimitivesSupported(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)){
VibrationEffect effect=VibrationEffect.startComposition().addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE).compose();
vibrator.vibrate(effect);
}
UiUtils.playVibrationEffectIfSupported(fragment.getActivity(), VibrationEffect.Composition.PRIMITIVE_QUICK_RISE);
}
return;
}