diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 8c798723..1722777a 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -10,7 +10,7 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 31 - versionCode 17 + versionCode 18 versionName "0.1" } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java new file mode 100644 index 00000000..8c588e46 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/ApiUtils.java @@ -0,0 +1,24 @@ +package org.joinmastodon.android.api; + +import com.google.gson.annotations.SerializedName; + +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +public class ApiUtils{ + private ApiUtils(){ + //no instance + } + + public static > List enumSetToStrings(EnumSet e, Class cls){ + return e.stream().map(ev->{ + try{ + SerializedName annotation=cls.getField(ev.name()).getAnnotation(SerializedName.class); + return annotation!=null ? annotation.value() : ev.name().toLowerCase(); + }catch(NoSuchFieldException x){ + throw new RuntimeException(x); + } + }).collect(Collectors.toList()); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java index aa7cf511..6b6a343e 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java @@ -129,7 +129,7 @@ public class CacheController{ Log.w(TAG, "getNotifications: corrupted notification object in database", x); } } - new GetNotifications(maxID, count, onlyMentions ? EnumSet.complementOf(EnumSet.of(Notification.Type.MENTION)): null) + new GetNotifications(maxID, count, onlyMentions ? EnumSet.of(Notification.Type.MENTION): EnumSet.allOf(Notification.Type.class)) .setCallback(new Callback<>(){ @Override public void onSuccess(List result){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java index a72e5840..c06f4151 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.app.ProgressDialog; import android.content.DialogInterface; import android.net.Uri; +import android.util.Log; import android.util.Pair; import com.google.gson.reflect.TypeToken; @@ -16,6 +17,7 @@ import org.joinmastodon.android.model.Token; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -27,6 +29,7 @@ import okhttp3.Call; import okhttp3.RequestBody; public abstract class MastodonAPIRequest extends APIRequest{ + private static final String TAG="MastodonAPIRequest"; private String domain; private AccountSession account; @@ -41,6 +44,7 @@ public abstract class MastodonAPIRequest extends APIRequest{ boolean canceled; Map headers; private ProgressDialog progressDialog; + protected boolean removeUnsupportedItems; public MastodonAPIRequest(HttpMethod method, String path, Class respClass){ this.path=path; @@ -150,9 +154,29 @@ public abstract class MastodonAPIRequest extends APIRequest{ if(respObj instanceof BaseModel){ ((BaseModel) respObj).postprocess(); }else if(respObj instanceof List){ - for(Object item : ((List) respObj)){ - if(item instanceof BaseModel) - ((BaseModel) item).postprocess(); + if(removeUnsupportedItems){ + Iterator itr=((List) respObj).iterator(); + while(itr.hasNext()){ + Object item=itr.next(); + if(item instanceof BaseModel){ + try{ + ((BaseModel) item).postprocess(); + }catch(ObjectValidationException x){ + Log.w(TAG, "Removing invalid object from list", x); + itr.remove(); + } + } + } + for(Object item:((List) respObj)){ + if(item instanceof BaseModel){ + ((BaseModel) item).postprocess(); + } + } + }else{ + for(Object item:((List) respObj)){ + if(item instanceof BaseModel) + ((BaseModel) item).postprocess(); + } } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/GetNotifications.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/GetNotifications.java index 9cf925cb..3c5a162f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/GetNotifications.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/notifications/GetNotifications.java @@ -3,6 +3,7 @@ package org.joinmastodon.android.api.requests.notifications; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; +import org.joinmastodon.android.api.ApiUtils; import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.model.Notification; @@ -10,18 +11,20 @@ import java.util.EnumSet; import java.util.List; public class GetNotifications extends MastodonAPIRequest>{ - public GetNotifications(String maxID, int limit, EnumSet excludeTypes){ + public GetNotifications(String maxID, int limit, EnumSet includeTypes){ super(HttpMethod.GET, "/notifications", new TypeToken<>(){}); if(maxID!=null) addQueryParameter("max_id", maxID); if(limit>0) addQueryParameter("limit", ""+limit); - if(excludeTypes!=null){ - for(Notification.Type nt:excludeTypes){ - try{ - addQueryParameter("exclude_types[]", nt.getDeclaringClass().getField(nt.name()).getAnnotation(SerializedName.class).value()); - }catch(NoSuchFieldException ignore){} + if(includeTypes!=null){ + for(String type:ApiUtils.enumSetToStrings(includeTypes, Notification.Type.class)){ + addQueryParameter("types[]", type); + } + for(String type:ApiUtils.enumSetToStrings(EnumSet.complementOf(includeTypes), Notification.Type.class)){ + addQueryParameter("exclude_types[]", type); } } + removeUnsupportedItems=true; } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index ecfc985a..9eef2ae4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -111,8 +111,8 @@ public class ComposeFragment extends ToolbarFragment implements OnBackPressedLis private static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE); // from https://github.com/mastodon/mastodon-ios/blob/main/Mastodon/Helper/MastodonRegex.swift - private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))|(^\\B:|\\s:)([a-zA-Z0-9_]+)"); - private static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))"); + private static final Pattern AUTO_COMPLETE_PATTERN=Pattern.compile("(? ids=new ArrayList<>(); + if(reportStatus!=null) + ids.add(reportStatus.id); + args.putStringArrayList("statusIDs", ids); + } args.putStringArrayList("ruleIDs", getArguments().getStringArrayList("ruleIDs")); args.putString("reason", getArguments().getString("reason")); Nav.go(getActivity(), ReportCommentFragment.class, args); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java index 01ef0323..8953fd2f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportCommentFragment.java @@ -66,11 +66,14 @@ public class ReportCommentFragment extends ToolbarFragment{ TextView title=view.findViewById(R.id.title); TextView subtitle=view.findViewById(R.id.subtitle); + TextView stepCounter=view.findViewById(R.id.step_counter); title.setText(R.string.report_comment_title); - subtitle.setText(R.string.report_comment_subtitle); + subtitle.setVisibility(View.GONE); + stepCounter.setText(getString(R.string.step_x_of_n, 3, 3)); btn=view.findViewById(R.id.btn_next); btn.setOnClickListener(this::onButtonClick); + view.findViewById(R.id.btn_back).setOnClickListener(this::onButtonClick); buttonBar=view.findViewById(R.id.button_bar); commentEdit=view.findViewById(R.id.text); @@ -98,7 +101,7 @@ public class ReportCommentFragment extends ToolbarFragment{ ReportReason reason=ReportReason.valueOf(getArguments().getString("reason")); ArrayList statusIDs=getArguments().getStringArrayList("statusIDs"); ArrayList ruleIDs=getArguments().getStringArrayList("ruleIDs"); - new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, commentEdit.getText().toString(), false) + new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), false) .setCallback(new Callback<>(){ @Override public void onSuccess(Object result){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java index 190ddb56..13580c17 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportReasonChoiceFragment.java @@ -14,7 +14,7 @@ import me.grishka.appkit.Nav; public class ReportReasonChoiceFragment extends BaseReportChoiceFragment{ @Override protected Item getHeaderItem(){ - return new Item(getString(R.string.report_choose_reason), getString(R.string.report_choose_reason_subtitle), null); + return new Item(reportStatus!=null ? getString(R.string.report_choose_reason) : getString(R.string.report_choose_reason_account, reportAccount.acct), getString(R.string.report_choose_reason_subtitle), null); } @Override @@ -43,6 +43,11 @@ public class ReportReasonChoiceFragment extends BaseReportChoiceFragment{ } } + @Override + protected int getStepNumber(){ + return 1; + } + @Subscribe public void onFinishReportFragments(FinishReportFragmentsEvent ev){ if(ev.reportAccountID.equals(reportAccount.id)) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java index 7d4f5a0e..b93eb786 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportRuleChoiceFragment.java @@ -40,6 +40,11 @@ public class ReportRuleChoiceFragment extends BaseReportChoiceFragment{ Nav.go(getActivity(), ReportAddPostsChoiceFragment.class, args); } + @Override + protected int getStepNumber(){ + return 1; + } + @Subscribe public void onFinishReportFragments(FinishReportFragmentsEvent ev){ if(ev.reportAccountID.equals(reportAccount.id)) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 3b650a72..f4a79a0d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -36,6 +36,9 @@ import org.joinmastodon.android.ui.text.CustomEmojiSpan; import java.io.File; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -58,6 +61,7 @@ import okhttp3.MediaType; public class UiUtils{ private static Handler mainHandler=new Handler(Looper.getMainLooper()); + private static final DateTimeFormatter DATE_FORMATTER_SHORT_WITH_YEAR=DateTimeFormatter.ofPattern("d MMM uuuu"), DATE_FORMATTER_SHORT=DateTimeFormatter.ofPattern("d MMM"); private UiUtils(){} @@ -80,7 +84,16 @@ public class UiUtils{ }else if(diff<3600_000L*24L){ return context.getString(R.string.time_hours, diff/3600_000L); }else{ - return context.getString(R.string.time_days, diff/(3600_000L*24L)); + int days=(int)(diff/(3600_000L*24L)); + if(days>30){ + ZonedDateTime dt=instant.atZone(ZoneId.systemDefault()); + if(dt.getYear()==ZonedDateTime.now().getYear()){ + return DATE_FORMATTER_SHORT.format(dt); + }else{ + return DATE_FORMATTER_SHORT_WITH_YEAR.format(dt); + } + } + return context.getString(R.string.time_days, days); } } diff --git a/mastodon/src/main/res/layout/button_bar_two.xml b/mastodon/src/main/res/layout/button_bar_two.xml new file mode 100644 index 00000000..37b8b716 --- /dev/null +++ b/mastodon/src/main/res/layout/button_bar_two.xml @@ -0,0 +1,35 @@ + + + +