This commit is contained in:
Grishka 2022-03-31 20:54:49 +03:00
parent c60bc253e5
commit 10655e4c98
20 changed files with 171 additions and 25 deletions

View File

@ -10,7 +10,7 @@ android {
applicationId "org.joinmastodon.android"
minSdk 23
targetSdk 31
versionCode 17
versionCode 18
versionName "0.1"
}

View File

@ -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 <E extends Enum<E>> List<String> enumSetToStrings(EnumSet<E> e, Class<E> 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());
}
}

View File

@ -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<Notification> result){

View File

@ -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<T> extends APIRequest<T>{
private static final String TAG="MastodonAPIRequest";
private String domain;
private AccountSession account;
@ -41,6 +44,7 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
boolean canceled;
Map<String, String> headers;
private ProgressDialog progressDialog;
protected boolean removeUnsupportedItems;
public MastodonAPIRequest(HttpMethod method, String path, Class<T> respClass){
this.path=path;
@ -150,9 +154,29 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
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();
}
}
}
}

View File

@ -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<List<Notification>>{
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> excludeTypes){
public GetNotifications(String maxID, int limit, EnumSet<Notification.Type> 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;
}
}

View File

@ -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("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))|(^\\B:|\\s:)([a-zA-Z0-9_]+)");
private static final Pattern HIGHLIGHT_PATTERN=Pattern.compile("(?<!\\w)(?:@([a-zA-Z0-9_]+)(@[a-zA-Z0-9_.-]+)?|#([^\\s.]+))");
private static final String VALID_URL_PATTERN_STRING =
"(" + // $1 total match

View File

@ -333,6 +333,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
page.loadData();
}
}
@Override
public void onPageScrollStateChanged(int state){
refreshLayout.setEnabled(state!=ViewPager2.SCROLL_STATE_DRAGGING);
}
});
return true;
}

View File

@ -58,6 +58,7 @@ public class InstanceRulesFragment extends AppKitFragment{
View headerView=inflater.inflate(R.layout.item_list_header, list, false);
TextView title=headerView.findViewById(R.id.title);
TextView subtitle=headerView.findViewById(R.id.subtitle);
headerView.findViewById(R.id.step_counter).setVisibility(View.GONE);
title.setText(R.string.instance_rules_title);
subtitle.setText(getString(R.string.instance_rules_subtitle, instance.uri));

View File

@ -78,8 +78,10 @@ public abstract class BaseReportChoiceFragment extends ToolbarFragment{
View headerView=inflater.inflate(R.layout.item_list_header, list, false);
TextView title=headerView.findViewById(R.id.title);
TextView subtitle=headerView.findViewById(R.id.subtitle);
TextView stepCounter=headerView.findViewById(R.id.step_counter);
title.setText(header.title);
subtitle.setText(header.subtitle);
stepCounter.setText(getString(R.string.step_x_of_n, getStepNumber(), 3));
adapter=new MergeRecyclerAdapter();
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
@ -98,6 +100,7 @@ public abstract class BaseReportChoiceFragment extends ToolbarFragment{
protected abstract Item getHeaderItem();
protected abstract void populateItems();
protected abstract void onButtonClick();
protected abstract int getStepNumber();
@Override
public void onApplyWindowInsets(WindowInsets insets){

View File

@ -32,6 +32,7 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -102,6 +103,7 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
btn=view.findViewById(R.id.btn_next);
btn.setEnabled(!selectedIDs.isEmpty());
btn.setOnClickListener(this::onButtonClick);
view.findViewById(R.id.btn_back).setOnClickListener(this::onButtonClick);
buttonBar=view.findViewById(R.id.button_bar);
list.addItemDecoration(new RecyclerView.ItemDecoration(){
@ -216,8 +218,10 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
View headerView=getActivity().getLayoutInflater().inflate(R.layout.item_list_header, list, false);
TextView title=headerView.findViewById(R.id.title);
TextView subtitle=headerView.findViewById(R.id.subtitle);
TextView stepCounter=headerView.findViewById(R.id.step_counter);
title.setText(R.string.report_choose_posts);
subtitle.setText(R.string.report_choose_posts_subtitle);
stepCounter.setText(getString(R.string.step_x_of_n, 2, 3));
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
@ -248,7 +252,14 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("reportAccount", Parcels.wrap(reportAccount));
args.putStringArrayList("statusIDs", selectedIDs);
if(v.getId()==R.id.btn_next){
args.putStringArrayList("statusIDs", selectedIDs);
}else{
ArrayList<String> 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);

View File

@ -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<String> statusIDs=getArguments().getStringArrayList("statusIDs");
ArrayList<String> 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){

View File

@ -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))

View File

@ -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))

View File

@ -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);
}
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:windowBackground"
android:outlineProvider="bounds"
android:elevation="3dp"
tools:showIn="@layout/fragment_report_posts">
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:minWidth="145dp"
style="?secondaryLargeButtonStyle"
android:text="@string/skip"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<Button
android:id="@+id/btn_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:minWidth="145dp"
style="?primaryLargeButtonStyle"
android:text="@string/next" />
</LinearLayout>

View File

@ -30,6 +30,6 @@
</ScrollView>
<include layout="@layout/button_bar_one"/>
<include layout="@layout/button_bar_two"/>
</LinearLayout>

View File

@ -32,6 +32,6 @@
</FrameLayout>
<include layout="@layout/button_bar_one"/>
<include layout="@layout/button_bar_two"/>
</me.grishka.appkit.views.FragmentRootLinearLayout>

View File

@ -9,6 +9,15 @@
android:paddingTop="32dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/step_counter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:textAppearance="@style/m3_title_medium"
android:textColor="?android:textColorSecondary"
tools:text="@string/step_x_of_n"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"

View File

@ -147,8 +147,9 @@
<item quantity="other">Discussed %d times</item>
</plurals>
<string name="report_title">Report %s</string>
<string name="report_choose_reason">Tell us what\'s going on with this post.</string>
<string name="report_choose_reason_subtitle">Choose the best match</string>
<string name="report_choose_reason">What\'s wrong with this post?</string>
<string name="report_choose_reason_account">What\'s wrong with %s?</string>
<string name="report_choose_reason_subtitle">Select the best match</string>
<string name="report_reason_personal">I don\'t like it</string>
<string name="report_reason_personal_subtitle">It is not something you want to see</string>
<string name="report_reason_spam">It\'s spam</string>
@ -160,9 +161,8 @@
<string name="report_choose_rule">Which rules are being violated?</string>
<string name="report_choose_rule_subtitle">Select all that apply</string>
<string name="report_choose_posts">Are there any posts that back up this report?</string>
<string name="report_choose_posts_subtitle">Optional. Select all that apply</string>
<string name="report_comment_title">Is there anything else you think we should know?</string>
<string name="report_comment_subtitle">Optional.</string>
<string name="report_choose_posts_subtitle">Select all that apply</string>
<string name="report_comment_title">Is there anything else we should know?</string>
<string name="report_comment_hint">Additional comments</string>
<string name="sending_report">Sending report…</string>
<string name="report_sent_title">Thanks for reporting, we\'ll look into this.</string>
@ -221,4 +221,6 @@
<string name="search_all">All</string>
<string name="search_people">People</string>
<string name="recent_searches">Recent searches</string>
<string name="step_x_of_n">Step %1$d of %2$d</string>
<string name="skip">Skip</string>
</resources>

View File

@ -42,6 +42,7 @@
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
<item name="android:popupMenuStyle">@style/Widget.Mastodon.PopupMenu</item>
<item name="android:actionOverflowMenuStyle">@style/Widget.Mastodon.PopupMenu</item>
</style>
<style name="Theme.Mastodon.Dark" parent="Theme.AppKit">
@ -87,6 +88,8 @@
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
<item name="android:popupMenuStyle">@style/Widget.Mastodon.PopupMenu</item>
<item name="android:actionOverflowMenuStyle">@style/Widget.Mastodon.PopupMenu</item>
</style>
<style name="Theme.Mastodon.AutoLightDark" parent="Theme.Mastodon.Light"/>