diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 888b3158d..89ee69a25 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -19,7 +19,7 @@ android { applicationId "org.joinmastodon.android.git" minSdk 23 targetSdk 33 - versionCode 46 + versionCode 47 versionName "1.1.5+${getGitHash()}" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW" diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/RegisterForPushNotifications.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/RegisterForPushNotifications.java index 5b096f2ba..b1ef8ace9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/RegisterForPushNotifications.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/RegisterForPushNotifications.java @@ -9,7 +9,7 @@ public class RegisterForPushNotifications extends MastodonAPIRequest{ + private final PushSubscription.Policy policy; + public UpdatePushSettings(PushSubscription.Alerts alerts, PushSubscription.Policy policy){ super(HttpMethod.PUT, "/push/subscription", PushSubscription.class); setRequestBody(new Request(alerts, policy)); + this.policy=policy; + } + + @Override + public void validateAndPostprocessResponse(PushSubscription respObj, Response httpResponse) throws IOException{ + super.validateAndPostprocessResponse(respObj, httpResponse); + respObj.policy=policy; } private static class Request{ public Data data=new Data(); + public PushSubscription.Policy policy; public Request(PushSubscription.Alerts alerts, PushSubscription.Policy policy){ this.data.alerts=alerts; - this.data.policy=policy; + this.policy=policy; } private static class Data{ public PushSubscription.Alerts alerts; - public PushSubscription.Policy policy; } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java index b7895ba87..6571307a6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java @@ -25,6 +25,7 @@ import com.squareup.otto.Subscribe; import org.joinmastodon.android.E; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.markers.SaveMarkers; import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.events.SelfUpdateStateChangedEvent; @@ -61,6 +62,7 @@ public class HomeTimelineFragment extends StatusListFragment{ private AnimatorSet currentNewPostsAnim; private String maxID; + private String lastSavedMarkerID; public HomeTimelineFragment(){ setListLayoutId(R.layout.recycler_fragment_with_fab); @@ -142,6 +144,29 @@ public class HomeTimelineFragment extends StatusListFragment{ } } + @Override + protected void onHidden(){ + super.onHidden(); + if(!data.isEmpty()){ + String topPostID=displayItems.get(list.getChildAdapterPosition(list.getChildAt(0))-getMainAdapterOffset()).parentID; + if(!topPostID.equals(lastSavedMarkerID)){ + lastSavedMarkerID=topPostID; + new SaveMarkers(topPostID, null) + .setCallback(new Callback<>(){ + @Override + public void onSuccess(SaveMarkers.Response result){ + } + + @Override + public void onError(ErrorResponse error){ + lastSavedMarkerID=null; + } + }) + .exec(accountID); + } + } + } + public void onStatusCreated(StatusCreatedEvent ev){ prependItems(Collections.singletonList(ev.status), true); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java b/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java index e70dfa4aa..82b6b62a6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java @@ -23,6 +23,7 @@ public class PushSubscription extends BaseModel implements Cloneable{ ", endpoint='"+endpoint+'\''+ ", alerts="+alerts+ ", serverKey='"+serverKey+'\''+ + ", policy="+policy+ '}'; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/PhotoStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/PhotoStatusDisplayItem.java index 964f57b4f..300077c24 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/PhotoStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/PhotoStatusDisplayItem.java @@ -1,7 +1,18 @@ package org.joinmastodon.android.ui.displayitems; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.app.Activity; +import android.text.TextUtils; +import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ScrollView; +import android.widget.TextView; import org.joinmastodon.android.R; import org.joinmastodon.android.fragments.BaseStatusListFragment; @@ -10,6 +21,7 @@ import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.PhotoLayoutHelper; import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; +import me.grishka.appkit.utils.CubicBezierInterpolator; public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{ public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos, PhotoLayoutHelper.TiledLayoutResult tiledLayout, PhotoLayoutHelper.TiledLayoutResult.Tile thisTile){ @@ -23,9 +35,109 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{ } public static class Holder extends ImageStatusDisplayItem.Holder{ + private final FrameLayout altTextWrapper; + private final TextView altTextButton; + private final View altTextScroller; + private final ImageButton altTextClose; + private final TextView altText; + + private boolean altTextShown; + private AnimatorSet currentAnim; public Holder(Activity activity, ViewGroup parent){ super(activity, R.layout.display_item_photo, parent); + altTextWrapper=findViewById(R.id.alt_text_wrapper); + altTextButton=findViewById(R.id.alt_button); + altTextScroller=findViewById(R.id.alt_text_scroller); + altTextClose=findViewById(R.id.alt_text_close); + altText=findViewById(R.id.alt_text); + + altTextButton.setOnClickListener(this::onShowHideClick); + altTextClose.setOnClickListener(this::onShowHideClick); +// altTextScroller.setNestedScrollingEnabled(true); + } + + @Override + public void onBind(ImageStatusDisplayItem item){ + super.onBind(item); + altTextShown=false; + if(currentAnim!=null) + currentAnim.cancel(); + altTextScroller.setVisibility(View.GONE); + altTextClose.setVisibility(View.GONE); + altTextButton.setVisibility(View.VISIBLE); + if(TextUtils.isEmpty(item.attachment.description)){ + altTextWrapper.setVisibility(View.GONE); + }else{ + altTextWrapper.setVisibility(View.VISIBLE); + altText.setText(item.attachment.description); + } + } + + private void onShowHideClick(View v){ + boolean show=v.getId()==R.id.alt_button; + + if(altTextShown==show) + return; + if(currentAnim!=null) + currentAnim.cancel(); + + altTextShown=show; + if(show){ + altTextScroller.setVisibility(View.VISIBLE); + altTextClose.setVisibility(View.VISIBLE); + }else{ + altTextButton.setVisibility(View.VISIBLE); + // Hide these views temporarily so FrameLayout measures correctly + altTextScroller.setVisibility(View.GONE); + altTextClose.setVisibility(View.GONE); + } + + // This is the current size... + int prevLeft=altTextWrapper.getLeft(); + int prevRight=altTextWrapper.getRight(); + int prevTop=altTextWrapper.getTop(); + altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){ + @Override + public boolean onPreDraw(){ + altTextWrapper.getViewTreeObserver().removeOnPreDrawListener(this); + + // ...and this is after the layout pass, right now the FrameLayout has its final size, but we animate that change + if(!show){ + // Show these views again so they're visible for the duration of the animation. + // No one would notice they were missing during measure/layout. + altTextScroller.setVisibility(View.VISIBLE); + altTextClose.setVisibility(View.VISIBLE); + } + AnimatorSet set=new AnimatorSet(); + set.playTogether( + ObjectAnimator.ofInt(altTextWrapper, "left", prevLeft, altTextWrapper.getLeft()), + ObjectAnimator.ofInt(altTextWrapper, "right", prevRight, altTextWrapper.getRight()), + ObjectAnimator.ofInt(altTextWrapper, "top", prevTop, altTextWrapper.getTop()), + ObjectAnimator.ofFloat(altTextButton, View.ALPHA, show ? 1f : 0f, show ? 0f : 1f), + ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f), + ObjectAnimator.ofFloat(altTextClose, View.ALPHA, show ? 0f : 1f, show ? 1f : 0f) + ); + set.setDuration(300); + set.setInterpolator(CubicBezierInterpolator.DEFAULT); + set.addListener(new AnimatorListenerAdapter(){ + @Override + public void onAnimationEnd(Animator animation){ + if(show){ + altTextButton.setVisibility(View.GONE); + }else{ + altTextScroller.setVisibility(View.GONE); + altTextClose.setVisibility(View.GONE); + } + currentAnim=null; + } + }); + set.start(); + currentAnim=set; + + return true; + } + }); } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/NestableScrollView.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/NestableScrollView.java new file mode 100644 index 000000000..7d5ffd60d --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/NestableScrollView.java @@ -0,0 +1,58 @@ +package org.joinmastodon.android.ui.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewConfiguration; +import android.widget.ScrollView; + +public class NestableScrollView extends ScrollView{ + private float downY, touchslop; + private boolean didDisallow; + + public NestableScrollView(Context context){ + super(context); + init(); + } + + public NestableScrollView(Context context, AttributeSet attrs){ + super(context, attrs); + init(); + } + + public NestableScrollView(Context context, AttributeSet attrs, int defStyleAttr){ + super(context, attrs, defStyleAttr); + init(); + } + + public NestableScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + private void init(){ + touchslop=ViewConfiguration.get(getContext()).getScaledTouchSlop(); + } + + @Override + public boolean onTouchEvent(MotionEvent ev){ + if(ev.getAction()==MotionEvent.ACTION_DOWN){ + if(canScrollVertically(-1) || canScrollVertically(1)){ + getParent().requestDisallowInterceptTouchEvent(true); + didDisallow=true; + }else{ + didDisallow=false; + } + downY=ev.getY(); + }else if(didDisallow && ev.getAction()==MotionEvent.ACTION_MOVE){ + if(Math.abs(downY-ev.getY())>=touchslop){ + if(!canScrollVertically((int)(downY-ev.getY()))){ + didDisallow=false; + getParent().requestDisallowInterceptTouchEvent(false); + } + } + } + return super.onTouchEvent(ev); + } +} diff --git a/mastodon/src/main/res/drawable/bg_image_alt_overlay.xml b/mastodon/src/main/res/drawable/bg_image_alt_overlay.xml new file mode 100644 index 000000000..56dc6e393 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_image_alt_overlay.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_baseline_close_24.xml b/mastodon/src/main/res/drawable/ic_baseline_close_24.xml new file mode 100644 index 000000000..844b6b62e --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_baseline_close_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/mastodon/src/main/res/layout/display_item_photo.xml b/mastodon/src/main/res/layout/display_item_photo.xml index 9c4c05ca8..00b0bd176 100644 --- a/mastodon/src/main/res/layout/display_item_photo.xml +++ b/mastodon/src/main/res/layout/display_item_photo.xml @@ -1,5 +1,6 @@ @@ -10,4 +11,62 @@ android:layout_gravity="center" android:scaleType="centerCrop"/> + + + + + + + + + + + + + + + + + + + \ No newline at end of file