From dd8abad8cac3726406045fcfe0e0bd073cc2941f Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Wed, 26 Feb 2020 20:41:02 +0100
Subject: [PATCH 01/42] When a plaintext share contains EXTRA_TEXT and a
distinct EXTRA_SUBJECT, use EXTRA_SUBJECT as the content warning for the
shared status (#1712)
---
.../tusky/components/compose/ComposeActivity.kt | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt
index d53bd6a57..87bc3e440 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt
@@ -207,15 +207,14 @@ class ComposeActivity : BaseActivity(),
if (action != null && action == Intent.ACTION_SEND) {
val subject = intent.getStringExtra(Intent.EXTRA_SUBJECT)
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
- val shareBody = if (subject != null && text != null) {
- if (subject != text && !text.contains(subject)) {
- String.format("%s\n%s", subject, text)
- } else {
- text
- }
- } else text ?: subject
+ val shareBody = text ?: subject
if (shareBody != null) {
+ if (!subject.isNullOrBlank() && subject !in shareBody) {
+ composeContentWarningField.setText(subject)
+ viewModel.showContentWarning.value = true
+ }
+
val start = composeEditField.selectionStart.coerceAtLeast(0)
val end = composeEditField.selectionEnd.coerceAtLeast(0)
val left = min(start, end)
From 3edc47aa4ac4008fdfaf88dd30f46692918bc2bd Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Mon, 2 Mar 2020 19:34:31 +0100
Subject: [PATCH 02/42] Add option to show link previews in timelines (#1681)
* Add option to show link previews in timelines.
Addresses #1075
* Indent cards in non-selected statuses when viewing threads
* Indent cards in timelines
* Fix clipping of right side of preview in timelines
---
.../tusky/PreferencesActivity.kt | 2 +-
.../tusky/adapter/NotificationsAdapter.java | 4 +-
.../tusky/adapter/StatusBaseViewHolder.java | 97 +++++++++++++++++
.../adapter/StatusDetailedViewHolder.java | 100 +-----------------
.../tusky/adapter/TimelineAdapter.java | 3 +-
.../conversation/ConversationsFragment.kt | 8 +-
.../fragments/ReportStatusesFragment.kt | 8 +-
.../fragments/SearchStatusesFragment.kt | 4 +-
.../tusky/fragment/NotificationsFragment.java | 4 +-
.../tusky/fragment/TimelineFragment.java | 6 +-
.../tusky/fragment/ViewThreadFragment.java | 6 +-
.../keylesspalace/tusky/util/CardViewMode.kt | 7 ++
.../tusky/util/StatusDisplayOptions.kt | 4 +-
app/src/main/res/layout/item_status.xml | 69 +++++++++++-
.../main/res/layout/item_status_detailed.xml | 4 +-
app/src/main/res/values/strings.xml | 1 +
app/src/main/res/xml/preferences.xml | 6 ++
17 files changed, 214 insertions(+), 119 deletions(-)
create mode 100644 app/src/main/java/com/keylesspalace/tusky/util/CardViewMode.kt
diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt
index 4fd0b5c69..157916dbd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt
@@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
}
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars",
- "useBlurhash" -> {
+ "useBlurhash", "showCardsInTimelines" -> {
restartActivitiesOnExit = true
}
"language" -> {
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
index 5882b64d4..61b6eecc5 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
@@ -44,6 +44,7 @@ import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.interfaces.LinkListener;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
+import com.keylesspalace.tusky.util.CardViewMode;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper;
@@ -230,7 +231,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
mediaPreviewEnabled,
statusDisplayOptions.useAbsoluteTime(),
statusDisplayOptions.showBotOverlay(),
- statusDisplayOptions.useBlurhash()
+ statusDisplayOptions.useBlurhash(),
+ CardViewMode.NONE
);
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
index d9acafe25..30baa5034 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
@@ -8,9 +8,11 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
@@ -22,14 +24,18 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.bitmap.CenterCrop;
+import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
import com.google.android.material.button.MaterialButton;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Attachment;
import com.keylesspalace.tusky.entity.Attachment.Focus;
import com.keylesspalace.tusky.entity.Attachment.MetaData;
+import com.keylesspalace.tusky.entity.Card;
import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
+import com.keylesspalace.tusky.util.CardViewMode;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.keylesspalace.tusky.util.HtmlUtils;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
@@ -86,6 +92,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private TextView pollDescription;
private Button pollButton;
+ private LinearLayout cardView;
+ private LinearLayout cardInfo;
+ private ImageView cardImage;
+ private TextView cardTitle;
+ private TextView cardDescription;
+ private TextView cardUrl;
private PollAdapter pollAdapter;
private SimpleDateFormat shortSdf;
@@ -143,6 +155,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
pollDescription = itemView.findViewById(R.id.status_poll_description);
pollButton = itemView.findViewById(R.id.status_poll_button);
+ cardView = itemView.findViewById(R.id.status_card_view);
+ cardInfo = itemView.findViewById(R.id.card_info);
+ cardImage = itemView.findViewById(R.id.card_image);
+ cardTitle = itemView.findViewById(R.id.card_title);
+ cardDescription = itemView.findViewById(R.id.card_description);
+ cardUrl = itemView.findViewById(R.id.card_link);
+
pollAdapter = new PollAdapter();
pollOptions.setAdapter(pollAdapter);
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
@@ -683,6 +702,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
hideSensitiveMediaWarning();
}
+ if (cardView != null) {
+ setupCard(status, statusDisplayOptions.cardViewMode());
+ }
+
setupButtons(listener, status.getSenderId());
setRebloggingEnabled(status.getRebloggingEnabled(), status.getVisibility());
@@ -911,6 +934,80 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
}
+ protected void setupCard(StatusViewData.Concrete status, CardViewMode cardViewMode) {
+ if (cardViewMode != CardViewMode.NONE && status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl())) {
+ final Card card = status.getCard();
+ cardView.setVisibility(View.VISIBLE);
+ cardTitle.setText(card.getTitle());
+ if (TextUtils.isEmpty(card.getDescription()) && TextUtils.isEmpty(card.getAuthorName())) {
+ cardDescription.setVisibility(View.GONE);
+ } else {
+ cardDescription.setVisibility(View.VISIBLE);
+ if (TextUtils.isEmpty(card.getDescription())) {
+ cardDescription.setText(card.getAuthorName());
+ } else {
+ cardDescription.setText(card.getDescription());
+ }
+ }
+
+ cardUrl.setText(card.getUrl());
+
+ if (!TextUtils.isEmpty(card.getImage())) {
+
+ int topLeftRadius = 0;
+ int topRightRadius = 0;
+ int bottomRightRadius = 0;
+ int bottomLeftRadius = 0;
+
+ int radius = cardImage.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.card_radius);
+
+ if (card.getWidth() > card.getHeight()) {
+ cardView.setOrientation(LinearLayout.VERTICAL);
+
+ cardImage.getLayoutParams().height = cardImage.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.card_image_vertical_height);
+ cardImage.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
+ cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
+ cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ topLeftRadius = radius;
+ topRightRadius = radius;
+ } else {
+ cardView.setOrientation(LinearLayout.HORIZONTAL);
+ cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
+ cardImage.getLayoutParams().width = cardImage.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.card_image_horizontal_width);
+ cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
+ topLeftRadius = radius;
+ bottomLeftRadius = radius;
+ }
+
+
+ Glide.with(cardImage)
+ .load(card.getImage())
+ .transform(
+ new CenterCrop(),
+ new GranularRoundedCorners(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius)
+ )
+ .into(cardImage);
+ } else {
+ cardView.setOrientation(LinearLayout.HORIZONTAL);
+ cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
+ cardImage.getLayoutParams().width = cardImage.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.card_image_horizontal_width);
+ cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
+ cardImage.setImageResource(R.drawable.card_image_placeholder);
+ }
+
+ cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext()));
+ cardView.setClipToOutline(true);
+ } else {
+ cardView.setVisibility(View.GONE);
+ }
+ }
+
private static String formatDuration(double durationInSeconds) {
int seconds = (int) Math.round(durationInSeconds) % 60;
int minutes = (int) durationInSeconds % 3600 / 60;
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java
index 06b8aaf6e..0d844b914 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusDetailedViewHolder.java
@@ -4,25 +4,18 @@ import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.resource.bitmap.CenterCrop;
-import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
import com.keylesspalace.tusky.R;
-import com.keylesspalace.tusky.entity.Card;
import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
+import com.keylesspalace.tusky.util.CardViewMode;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.StatusDisplayOptions;
import com.keylesspalace.tusky.viewdata.StatusViewData;
@@ -33,27 +26,13 @@ import java.util.Date;
class StatusDetailedViewHolder extends StatusBaseViewHolder {
private TextView reblogs;
private TextView favourites;
- private LinearLayout cardView;
- private LinearLayout cardInfo;
- private ImageView cardImage;
- private TextView cardTitle;
- private TextView cardDescription;
- private TextView cardUrl;
private View infoDivider;
StatusDetailedViewHolder(View view) {
super(view);
reblogs = view.findViewById(R.id.status_reblogs);
favourites = view.findViewById(R.id.status_favourites);
- cardView = view.findViewById(R.id.card_view);
- cardInfo = view.findViewById(R.id.card_info);
- cardImage = view.findViewById(R.id.card_image);
- cardTitle = view.findViewById(R.id.card_title);
- cardDescription = view.findViewById(R.id.card_description);
- cardUrl = view.findViewById(R.id.card_link);
infoDivider = view.findViewById(R.id.status_info_divider);
-
- cardView.setClipToOutline(true);
}
@Override
@@ -127,6 +106,7 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
StatusDisplayOptions statusDisplayOptions,
@Nullable Object payloads) {
super.setupWithStatus(status, listener, statusDisplayOptions, payloads);
+ setupCard(status, CardViewMode.FULL_WIDTH); // Always show card for detailed status
if (payloads == null) {
setReblogAndFavCount(status.getReblogsCount(), status.getFavouritesCount(), listener);
@@ -145,82 +125,6 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
content.setOnLongClickListener(longClickListener);
contentWarningDescription.setOnLongClickListener(longClickListener);
-
- if (status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl())) {
- final Card card = status.getCard();
- cardView.setVisibility(View.VISIBLE);
- cardTitle.setText(card.getTitle());
- if (TextUtils.isEmpty(card.getDescription()) && TextUtils.isEmpty(card.getAuthorName())) {
- cardDescription.setVisibility(View.GONE);
- } else {
- cardDescription.setVisibility(View.VISIBLE);
- if (TextUtils.isEmpty(card.getDescription())) {
- cardDescription.setText(card.getAuthorName());
- } else {
- cardDescription.setText(card.getDescription());
- }
- }
-
- cardUrl.setText(card.getUrl());
-
- if (!TextUtils.isEmpty(card.getImage())) {
-
- int topLeftRadius = 0;
- int topRightRadius = 0;
- int bottomRightRadius = 0;
- int bottomLeftRadius = 0;
-
- int radius = cardImage.getContext().getResources()
- .getDimensionPixelSize(R.dimen.card_radius);
-
- if (card.getWidth() > card.getHeight()) {
- cardView.setOrientation(LinearLayout.VERTICAL);
-
- cardImage.getLayoutParams().height = cardImage.getContext().getResources()
- .getDimensionPixelSize(R.dimen.card_image_vertical_height);
- cardImage.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
- cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
- cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
- topLeftRadius = radius;
- topRightRadius = radius;
- } else {
- cardView.setOrientation(LinearLayout.HORIZONTAL);
- cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
- cardImage.getLayoutParams().width = cardImage.getContext().getResources()
- .getDimensionPixelSize(R.dimen.card_image_horizontal_width);
- cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
- cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
- topLeftRadius = radius;
- bottomLeftRadius = radius;
- }
-
-
- Glide.with(cardImage)
- .load(card.getImage())
- .transform(
- new CenterCrop(),
- new GranularRoundedCorners(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius)
- )
- .into(cardImage);
-
- } else {
- cardView.setOrientation(LinearLayout.HORIZONTAL);
- cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
- cardImage.getLayoutParams().width = cardImage.getContext().getResources()
- .getDimensionPixelSize(R.dimen.card_image_horizontal_width);
- cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
- cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
-
- cardImage.setImageResource(R.drawable.card_image_placeholder);
-
- }
-
- cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext()));
-
- } else {
- cardView.setVisibility(View.GONE);
- }
-
setStatusVisibility(status.getVisibility());
}
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java
index 464d2bc63..307ddfc50 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java
@@ -63,7 +63,8 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
mediaPreviewEnabled,
statusDisplayOptions.useAbsoluteTime(),
statusDisplayOptions.showBotOverlay(),
- statusDisplayOptions.useBlurhash()
+ statusDisplayOptions.useBlurhash(),
+ statusDisplayOptions.cardViewMode()
);
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
index fbc342810..65816c18a 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
@@ -36,10 +36,7 @@ import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.fragment.SFragment
import com.keylesspalace.tusky.interfaces.ReselectableFragment
import com.keylesspalace.tusky.interfaces.StatusActionListener
-import com.keylesspalace.tusky.util.NetworkState
-import com.keylesspalace.tusky.util.StatusDisplayOptions
-import com.keylesspalace.tusky.util.ThemeUtils
-import com.keylesspalace.tusky.util.hide
+import com.keylesspalace.tusky.util.*
import kotlinx.android.synthetic.main.fragment_timeline.*
import javax.inject.Inject
@@ -68,7 +65,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
- useBlurhash = preferences.getBoolean("useBlurhash", true)
+ useBlurhash = preferences.getBoolean("useBlurhash", true),
+ cardViewMode = CardViewMode.NONE
)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
index 3e9bd622d..f30406ce1 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
@@ -43,10 +43,7 @@ import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.entity.Status
-import com.keylesspalace.tusky.util.StatusDisplayOptions
-import com.keylesspalace.tusky.util.ThemeUtils
-import com.keylesspalace.tusky.util.hide
-import com.keylesspalace.tusky.util.show
+import com.keylesspalace.tusky.util.*
import com.keylesspalace.tusky.viewdata.AttachmentViewData
import kotlinx.android.synthetic.main.fragment_report_statuses.*
import javax.inject.Inject
@@ -119,7 +116,8 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler {
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
showBotOverlay = false,
- useBlurhash = preferences.getBoolean("useBlurhash", true)
+ useBlurhash = preferences.getBoolean("useBlurhash", true),
+ cardViewMode = CardViewMode.NONE
)
adapter = StatusesAdapter(statusDisplayOptions,
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
index c384d123c..3ef3d5242 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
@@ -51,6 +51,7 @@ import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
import com.keylesspalace.tusky.interfaces.StatusActionListener
+import com.keylesspalace.tusky.util.CardViewMode
import com.keylesspalace.tusky.util.NetworkState
import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.viewdata.AttachmentViewData
@@ -79,7 +80,8 @@ class SearchStatusesFragment : SearchFragment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_status_detailed.xml b/app/src/main/res/layout/item_status_detailed.xml
index 0436b0609..8e1886f54 100644
--- a/app/src/main/res/layout/item_status_detailed.xml
+++ b/app/src/main/res/layout/item_status_detailed.xml
@@ -131,7 +131,7 @@
tools:text="Status content. Can be pretty long. " />
+ app:layout_constraintTop_toBottomOf="@id/status_card_view">
You don\'t have any drafts.
You don\'t have any scheduled statuses.
Mastodon has a minimum scheduling interval of 5 minutes.
+ Show link previews in timelines
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 6dc1faa9c..d2e2bfae8 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -72,6 +72,12 @@
android:title="@string/pref_title_show_notifications_filter"
app:singleLineTitle="false" />
+
+
From 712f61b15ad46c1a96ed362acf64286193c68b1a Mon Sep 17 00:00:00 2001
From: Ivan Kupalov
Date: Mon, 2 Mar 2020 19:36:44 +0100
Subject: [PATCH 03/42] Add "more" popup to a11y menu, fix #1710 (#1714)
---
.../tusky/util/ListStatusAccessibilityDelegate.kt | 10 ++++++++++
app/src/main/res/values/actions.xml | 1 +
2 files changed, 11 insertions(+)
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt b/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt
index 8373d2bc3..8594dfc60 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/util/ListStatusAccessibilityDelegate.kt
@@ -82,6 +82,8 @@ class ListStatusAccessibilityDelegate(
}
if (status.reblogsCount > 0) info.addAction(openRebloggedByAction)
if (status.favouritesCount > 0) info.addAction(openFavsAction)
+
+ info.addAction(moreAction)
}
}
@@ -150,6 +152,9 @@ class ListStatusAccessibilityDelegate(
interrupt()
statusActionListener.onShowFavs(pos)
}
+ R.id.action_more -> {
+ statusActionListener.onMore(host, pos)
+ }
else -> return super.performAccessibilityAction(host, action, args)
}
return true
@@ -311,5 +316,10 @@ class ListStatusAccessibilityDelegate(
R.id.action_open_faved_by,
context.getString(R.string.action_open_faved_by))
+ private val moreAction = AccessibilityActionCompat(
+ R.id.action_more,
+ context.getString(R.string.action_more)
+ )
+
private data class LinkSpanInfo(val text: String, val link: String)
}
\ No newline at end of file
diff --git a/app/src/main/res/values/actions.xml b/app/src/main/res/values/actions.xml
index a3a3168f7..0d78f2d17 100644
--- a/app/src/main/res/values/actions.xml
+++ b/app/src/main/res/values/actions.xml
@@ -22,4 +22,5 @@
+
\ No newline at end of file
From 0c159e587d83737b46e1f02c371beeb5f8a3f79d Mon Sep 17 00:00:00 2001
From: Konrad Pozniak
Date: Mon, 2 Mar 2020 19:58:15 +0100
Subject: [PATCH 04/42] upgrade Gradle and Android plugin (#1709)
* upgrade Gradle and Android plugin
* upgrade Android Gradle plugin to 3.6.1
---
build.gradle | 2 +-
gradle.properties | 2 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/build.gradle b/build.gradle
index 17952bd8d..a71af6f14 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath 'com.android.tools.build:gradle:3.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/gradle.properties b/gradle.properties
index d3ea8914f..8144ece09 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,5 +15,5 @@ org.gradle.jvmargs=-Xmx4096m
# use parallel execution
org.gradle.parallel=true
-android.enableUnitTestBinaryResources=true
android.enableR8.fullMode=true
+android.useAndroidX=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4e1cc9db6..6254d2d4a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From 67c20326f97f2f980d588d4f62828f94473c079f Mon Sep 17 00:00:00 2001
From: Ivan Kupalov
Date: Tue, 3 Mar 2020 21:27:26 +0100
Subject: [PATCH 05/42] Add options to confirm reblog/unreblog actions, close
#460 (#1650)
---
app/build.gradle | 2 +-
.../tusky/PreferencesActivity.kt | 2 +-
.../tusky/adapter/NotificationsAdapter.java | 3 +-
.../tusky/adapter/StatusBaseViewHolder.java | 43 ++++++++++++++++---
.../tusky/adapter/TimelineAdapter.java | 3 +-
.../conversation/ConversationViewHolder.java | 3 +-
.../conversation/ConversationsFragment.kt | 3 +-
.../fragments/ReportStatusesFragment.kt | 3 +-
.../fragments/SearchStatusesFragment.kt | 5 ++-
.../tusky/fragment/NotificationsFragment.java | 3 +-
.../tusky/fragment/TimelineFragment.java | 7 ++-
.../tusky/fragment/ViewThreadFragment.java | 3 +-
.../tusky/util/StatusDisplayOptions.kt | 4 +-
app/src/main/res/values/strings.xml | 1 +
app/src/main/res/xml/preferences.xml | 6 +++
15 files changed, 71 insertions(+), 20 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 11f4474f6..1defe9f78 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -155,7 +155,7 @@ dependencies {
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
- implementation "com.github.connyduck:sparkbutton:3.0.0"
+ implementation "com.github.connyduck:sparkbutton:4.0.0"
implementation "com.github.chrisbanes:PhotoView:2.3.0"
diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt
index 157916dbd..4adfc4e98 100644
--- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt
@@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
}
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars",
- "useBlurhash", "showCardsInTimelines" -> {
+ "useBlurhash", "showCardsInTimelines", "confirmReblogs" -> {
restartActivitiesOnExit = true
}
"language" -> {
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
index 61b6eecc5..b12bab338 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
@@ -232,7 +232,8 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
statusDisplayOptions.useAbsoluteTime(),
statusDisplayOptions.showBotOverlay(),
statusDisplayOptions.useBlurhash(),
- CardViewMode.NONE
+ CardViewMode.NONE,
+ statusDisplayOptions.confirmReblogs()
);
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
index 30baa5034..ad0e6573d 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
@@ -19,6 +19,7 @@ import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -209,7 +210,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
contentWarningDescription.setVisibility(View.VISIBLE);
contentWarningButton.setVisibility(View.VISIBLE);
setContentWarningButtonText(expanded);
- contentWarningButton.setOnClickListener( view -> {
+ contentWarningButton.setOnClickListener(view -> {
contentWarningDescription.invalidate();
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
listener.onExpandedChange(!expanded, getAdapterPosition());
@@ -227,7 +228,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
}
private void setContentWarningButtonText(boolean expanded) {
- if(expanded) {
+ if (expanded) {
contentWarningButton.setText(R.string.status_content_warning_show_less);
} else {
contentWarningButton.setText(R.string.status_content_warning_show_more);
@@ -610,7 +611,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
sensitiveMediaShow.setVisibility(View.GONE);
}
- protected void setupButtons(final StatusActionListener listener, final String accountId) {
+ protected void setupButtons(final StatusActionListener listener, final String accountId,
+ final String statusContent,
+ StatusDisplayOptions statusDisplayOptions) {
avatar.setOnClickListener(v -> listener.onViewAccount(accountId));
replyButton.setOnClickListener(v -> {
@@ -623,7 +626,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
reblogButton.setEventListener((button, buttonState) -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
- listener.onReblog(buttonState, position);
+ listener.onReblog(!buttonState, position);
+ }
+ if (statusDisplayOptions.confirmReblogs()) {
+ showConfirmReblogDialog(listener, statusContent, buttonState, position);
+ return false;
+ } else {
+ return true;
}
});
}
@@ -631,15 +640,17 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
favouriteButton.setEventListener((button, buttonState) -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
- listener.onFavourite(buttonState, position);
+ listener.onFavourite(!buttonState, position);
}
+ return true;
});
bookmarkButton.setEventListener((button, buttonState) -> {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
- listener.onBookmark(buttonState, position);
+ listener.onBookmark(!buttonState, position);
}
+ return true;
});
moreButton.setOnClickListener(v -> {
@@ -662,6 +673,23 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
itemView.setOnClickListener(viewThreadListener);
}
+ private void showConfirmReblogDialog(StatusActionListener listener,
+ String statusContent,
+ boolean buttonState,
+ int position) {
+ int okButtonTextId = buttonState ? R.string.action_unreblog : R.string.action_reblog;
+ new AlertDialog.Builder(reblogButton.getContext())
+ .setMessage(statusContent)
+ .setPositiveButton(okButtonTextId, (__, ___) -> {
+ listener.onReblog(!buttonState, position);
+ if (!buttonState) {
+ // Play animation only when it's reblog, not unreblog
+ reblogButton.playAnimation();
+ }
+ })
+ .show();
+ }
+
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
StatusDisplayOptions statusDisplayOptions) {
this.setupWithStatus(status, listener, statusDisplayOptions, null);
@@ -706,7 +734,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
setupCard(status, statusDisplayOptions.cardViewMode());
}
- setupButtons(listener, status.getSenderId());
+ setupButtons(listener, status.getSenderId(), status.getContent().toString(),
+ statusDisplayOptions);
setRebloggingEnabled(status.getRebloggingEnabled(), status.getVisibility());
setSpoilerAndContent(status.isExpanded(), status.getContent(), status.getSpoilerText(), status.getMentions(), status.getStatusEmojis(), status.getPoll(), statusDisplayOptions, listener);
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java
index 307ddfc50..91ec25e2f 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/TimelineAdapter.java
@@ -64,7 +64,8 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
statusDisplayOptions.useAbsoluteTime(),
statusDisplayOptions.showBotOverlay(),
statusDisplayOptions.useBlurhash(),
- statusDisplayOptions.cardViewMode()
+ statusDisplayOptions.cardViewMode(),
+ statusDisplayOptions.confirmReblogs()
);
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java
index 365575994..6727156d0 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationViewHolder.java
@@ -104,7 +104,8 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
hideSensitiveMediaWarning();
}
- setupButtons(listener, account.getId());
+ setupButtons(listener, account.getId(), status.getContent().toString(),
+ statusDisplayOptions);
setSpoilerAndContent(status.getExpanded(), status.getContent(), status.getSpoilerText(),
status.getMentions(), status.getEmojis(),
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
index 65816c18a..a91ed2f08 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
@@ -66,7 +66,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
useBlurhash = preferences.getBoolean("useBlurhash", true),
- cardViewMode = CardViewMode.NONE
+ cardViewMode = CardViewMode.NONE,
+ confirmReblogs = preferences.getBoolean("confirmReblogs", true)
)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
index f30406ce1..dad6fe1f5 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
@@ -117,7 +117,8 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler {
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
showBotOverlay = false,
useBlurhash = preferences.getBoolean("useBlurhash", true),
- cardViewMode = CardViewMode.NONE
+ cardViewMode = CardViewMode.NONE,
+ confirmReblogs = preferences.getBoolean("confirmReblogs", true)
)
adapter = StatusesAdapter(statusDisplayOptions,
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
index 3ef3d5242..ac171b9e7 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
@@ -71,7 +71,7 @@ class SearchStatusesFragment : SearchFragment, *> {
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
@@ -81,7 +81,8 @@ class SearchStatusesFragment : SearchFragmentYou don\'t have any scheduled statuses.
Mastodon has a minimum scheduling interval of 5 minutes.
Show link previews in timelines
+ Show confirmation dialog before boosting
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index d2e2bfae8..b04ccacc7 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -78,6 +78,12 @@
android:title="@string/pref_title_show_cards_in_timelines"
app:singleLineTitle="false" />
+
+
From 75336fe66ab7ad46685f6dc4cc389c431d5dc0c2 Mon Sep 17 00:00:00 2001
From: Anonymous
Date: Fri, 28 Feb 2020 12:41:46 +0000
Subject: [PATCH 06/42] Translated using Weblate (German)
Currently translated at 87.5% (7 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/de/
---
fastlane/metadata/android/de/changelogs/68.txt | 3 +++
fastlane/metadata/android/de/changelogs/70.txt | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)
create mode 100644 fastlane/metadata/android/de/changelogs/68.txt
diff --git a/fastlane/metadata/android/de/changelogs/68.txt b/fastlane/metadata/android/de/changelogs/68.txt
new file mode 100644
index 000000000..dee85857a
--- /dev/null
+++ b/fastlane/metadata/android/de/changelogs/68.txt
@@ -0,0 +1,3 @@
+Tusky v9.1
+
+Dieses Release stellt die Kompatibilität mit Mastodon 3 sicher und verbessert Geschwindigkeit und Stabilität der App.
diff --git a/fastlane/metadata/android/de/changelogs/70.txt b/fastlane/metadata/android/de/changelogs/70.txt
index d674795ad..749d0a9e8 100644
--- a/fastlane/metadata/android/de/changelogs/70.txt
+++ b/fastlane/metadata/android/de/changelogs/70.txt
@@ -5,4 +5,4 @@ Tusky v10.0
- Du kannst jetzt Listen auf dem Hauptbildschirm anzeigen.
- Du kannst jetzt Audio-Anhänge versenden.
-Und viele andere Verbesserungen und Fehlerkorrekturen!
\ No newline at end of file
+Und viele andere Verbesserungen und Fehlerkorrekturen!
From 64e500c5b398a216876595da0dbe33b79e6d5c6d Mon Sep 17 00:00:00 2001
From: Danial Behzadi
Date: Thu, 27 Feb 2020 12:36:19 +0000
Subject: [PATCH 07/42] Translated using Weblate (Persian)
Currently translated at 100.0% (8 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/fa/
---
fastlane/metadata/android/fa/changelogs/70.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 fastlane/metadata/android/fa/changelogs/70.txt
diff --git a/fastlane/metadata/android/fa/changelogs/70.txt b/fastlane/metadata/android/fa/changelogs/70.txt
new file mode 100644
index 000000000..f124dd4f7
--- /dev/null
+++ b/fastlane/metadata/android/fa/changelogs/70.txt
@@ -0,0 +1,8 @@
+تاسکی نگارش ۱۰.۰
+
+- اکنون میتوانید بوقها را نشان کرده و نشانکهایتان را فهرست کنید.
+- اکنون میتوانید بوقها را زمانبندی کنید. توجّه داشته باشید که زمان گزیدهتان باید لااقل ۵ دقیقه بعد باشد.
+- اکنون میتوانید فهرستها را به صفحهٔ اصلی بیفزایید.
+- اکنون میتوانید پیوستهای صوتی بفرستید.
+
+و بسیاری از بهبودها و رفع اشکالهای ریز!
From 7921a85d62dcd693d5c051cb124a876895d7a5f6 Mon Sep 17 00:00:00 2001
From: knuxify
Date: Wed, 26 Feb 2020 13:58:14 +0000
Subject: [PATCH 08/42] Translated using Weblate (Polish)
Currently translated at 100.0% (8 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/pl/
---
fastlane/metadata/android/pl/changelogs/70.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 fastlane/metadata/android/pl/changelogs/70.txt
diff --git a/fastlane/metadata/android/pl/changelogs/70.txt b/fastlane/metadata/android/pl/changelogs/70.txt
new file mode 100644
index 000000000..a1723c287
--- /dev/null
+++ b/fastlane/metadata/android/pl/changelogs/70.txt
@@ -0,0 +1,8 @@
+Tusky v10.0
+
+- Dodano wsparcie dla zakładek. Możesz teraz dodawać wpisy do zakładek i przeglądać zakładki w Tusky.
+- Możesz teraz zaplanować wpisy w Tusky. (Uwaga: wybrany czas do wysłania zaplanowanego wpisu musi wynosić przynajmniej 5 minut)
+- Możesz teraz dodawać listy do ekranu głównego.
+- Możesz teraz załączać pliki audio do wpisów.
+
+Plus wiele innych małych ulepszeń i poprawek!
From 522a790804e7170fbb938f04fe3522fd79da1e0d Mon Sep 17 00:00:00 2001
From: tolstoevsky
Date: Wed, 26 Feb 2020 08:24:39 +0000
Subject: [PATCH 09/42] Translated using Weblate (Russian)
Currently translated at 100.0% (8 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/ru/
---
fastlane/metadata/android/ru/changelogs/70.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 fastlane/metadata/android/ru/changelogs/70.txt
diff --git a/fastlane/metadata/android/ru/changelogs/70.txt b/fastlane/metadata/android/ru/changelogs/70.txt
new file mode 100644
index 000000000..7b67dc66a
--- /dev/null
+++ b/fastlane/metadata/android/ru/changelogs/70.txt
@@ -0,0 +1,8 @@
+Tusky v10.0
+
+- Теперь вы можете добавлять статусы в закладки и просматривать списки закладок в Tusky.
+- Теперь вы можете отправлять отложенные посты из Tusky. Учтите, что время отправки должно быть не менее, чем на пять минут позже текущего.
+- Теперь вы можете добавлять списки на главный экран.
+- Теперь вы можете публиковать аудио-вложения из Tusky.
+
+И множество других мелких улучшений и исправлений!
From e6dc91487b3c24ad181eb04f24ecd969c93b6137 Mon Sep 17 00:00:00 2001
From: Selyan Slimane Amiri
Date: Fri, 28 Feb 2020 15:14:28 +0000
Subject: [PATCH 10/42] Translated using Weblate (Kabyle)
Currently translated at 25.0% (2 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/kab/
---
fastlane/metadata/android/kab/short_description.txt | 1 +
fastlane/metadata/android/kab/title.txt | 1 +
2 files changed, 2 insertions(+)
create mode 100644 fastlane/metadata/android/kab/short_description.txt
create mode 100644 fastlane/metadata/android/kab/title.txt
diff --git a/fastlane/metadata/android/kab/short_description.txt b/fastlane/metadata/android/kab/short_description.txt
new file mode 100644
index 000000000..b770c5940
--- /dev/null
+++ b/fastlane/metadata/android/kab/short_description.txt
@@ -0,0 +1 @@
+Asnas isefraken aget-imiḍanen n uẓeṭṭa n tmetti Mastodon
diff --git a/fastlane/metadata/android/kab/title.txt b/fastlane/metadata/android/kab/title.txt
new file mode 100644
index 000000000..0238ffc0a
--- /dev/null
+++ b/fastlane/metadata/android/kab/title.txt
@@ -0,0 +1 @@
+Tusky
From 1239ad114ca18b87bd04f1d32b1520a71a314b51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?=
Date: Mon, 2 Mar 2020 14:14:09 +0000
Subject: [PATCH 11/42] Translated using Weblate (Icelandic)
Currently translated at 100.0% (8 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/is/
---
fastlane/metadata/android/is/changelogs/70.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 fastlane/metadata/android/is/changelogs/70.txt
diff --git a/fastlane/metadata/android/is/changelogs/70.txt b/fastlane/metadata/android/is/changelogs/70.txt
new file mode 100644
index 000000000..1525eefdd
--- /dev/null
+++ b/fastlane/metadata/android/is/changelogs/70.txt
@@ -0,0 +1,8 @@
+Tusky v10.0
+
+- Þú getur núna bákamerkt stöðufærslur og gert lista með bókamerkjunum þínum í Tusky.
+- Þú getur núna sett tíst á áætlun í Tusky. Athugaðu að tíminn sem þú velur þarf að vera í það minnsta efti 5 mínútur.
+- Þú getur núna bætt listum á aðalskjáinn.
+- Þú getur núna sent in hljóðskrár sem viðhengi í Tusky.
+
+Auk hellings af smærri lagfæringum og bætingum!
From 69f9145a23170a24c7dc419db0b543dfd9db4fa4 Mon Sep 17 00:00:00 2001
From: Anonymous
Date: Thu, 5 Mar 2020 08:41:57 +0000
Subject: [PATCH 12/42] Translated using Weblate (Japanese)
Currently translated at 50.0% (4 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/ja/
---
fastlane/metadata/android/ja/changelogs/70.txt | 8 ++++++++
fastlane/metadata/android/ja/full_description.txt | 12 ++++++++++++
2 files changed, 20 insertions(+)
create mode 100644 fastlane/metadata/android/ja/changelogs/70.txt
create mode 100644 fastlane/metadata/android/ja/full_description.txt
diff --git a/fastlane/metadata/android/ja/changelogs/70.txt b/fastlane/metadata/android/ja/changelogs/70.txt
new file mode 100644
index 000000000..f7e42126f
--- /dev/null
+++ b/fastlane/metadata/android/ja/changelogs/70.txt
@@ -0,0 +1,8 @@
+Tusky v10.0
+
+- Tuskyでブックマークの登録、表示が行えるようになりました。
+- You can now schedule toots with Tusky. Note that the time you select has to be at least 5 minutes in the future.
+- You can now add lists to the main screen.
+- Tuskyで音声ファイルを投稿出来るようになりました。
+
+その他、多くの細かな改善とバグの修正を行いました!
diff --git a/fastlane/metadata/android/ja/full_description.txt b/fastlane/metadata/android/ja/full_description.txt
new file mode 100644
index 000000000..353726c18
--- /dev/null
+++ b/fastlane/metadata/android/ja/full_description.txt
@@ -0,0 +1,12 @@
+Tuskyは、自由でオープンソースのソーシャルネットワークサーバーであるMastodon用の動作の高速なクライアントです。
+
+• マテリアルデザイン
+• ほとんどのMastodon APIに対応
+• マルチアカウント対応
+• 時刻に基づいて自動変更可能なダークテーマとライトテーマ
+• 下書き - トゥートを作成し、保存しておく
+• 異なる絵文字形式を選択可能
+• すべての画面サイズに最適化
+• 完全にオープンソース - Googleサービスのような自由でない依存関係はありません
+
+Mastodonの詳細についてはこちらをご覧ください:https://joinmastodon.org/
From dfab2cb0008d2469580348a0d12a0a546c51c462 Mon Sep 17 00:00:00 2001
From: ButterflyOfFire
Date: Fri, 6 Mar 2020 11:27:21 +0000
Subject: [PATCH 13/42] Translated using Weblate (Kabyle)
Currently translated at 58.5% (240 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (Kabyle)
Currently translated at 53.2% (218 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (French)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/fr/
Translated using Weblate (Kabyle)
Currently translated at 53.2% (218 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (Arabic)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/ar/
Translated using Weblate (Kabyle)
Currently translated at 53.2% (218 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (Kabyle)
Currently translated at 52.7% (216 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (French)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/fr/
---
app/src/main/res/values-ar/strings.xml | 4 +-
app/src/main/res/values-fr/strings.xml | 4 +-
app/src/main/res/values-kab/strings.xml | 154 +++++++++++++++---------
3 files changed, 101 insertions(+), 61 deletions(-)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 9089cb5f8..c40e7585c 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -501,4 +501,6 @@
ليس لديك أية منشورات مُبرمَجة للنشر.
يجب أن يكون حجم الملفات الصوتية أقل مِن 40 ميغابايت.
-
+ تُقدّر أدنى فترة لبرمجة النشر في ماستدون بـ 5 دقائق.
+
+
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 49cf48b24..36bef8c9e 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -371,7 +371,7 @@
Filtrer
Appliquer
- Écrire un pouet
+ Rédiger un pouet
Écrire
Afficher l\'indicateur de robots
@@ -486,4 +486,6 @@
Vous n’avez aucun brouillon.
Vous n’avez aucun pouet planifié.
+L’intervalle minimum de planification sur Mastodon est de 5 minutes.
+
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index 09780b592..b91bee1ed 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -1,22 +1,22 @@
-Qqen γer Maṣṭudun
+Qqen ɣer Maṣṭudun
Ismenyifen
Irewwayen
- Ffeγ
- Iγewwaṛen
- Iγewwaṛen n umiḍan
- Ẓreg amaγnu
+ Ffeɣ
+ Iɣewwaṛen
+ Iɣewwaṛen n umiḍan
+ Ẓreg amaɣnu
Nadi
- Γef
- Umuγen
- Umuγen
- Tijewwiqt-ik aṭas i γuzzifet!
+ Ɣef
+ Umuɣen
+ Umuɣen
+ Tijewwiqt-ik aṭas i ɣuzzifet!
Agejdan
Iccaren
Tijewwiqt
Iznan
S tririyin
- Ẓreg amaγnu-ik
+ Ẓreg amaɣnu-ik
\@%s
Zeṛ ugar
Zeṛ kra kan
@@ -35,13 +35,13 @@
JEWWEQ!
Ɛreḍ tikkelt-nniḍen
Derreɛ
- Amaγnu
+ Amaɣnu
Ismenyifen
Ldi deg uminig
Bḍu
Sgugem
Irewwayen
- Sken-ed ismenyifen
+ Sken-d ismenyifen
Ismenyifen
@@ -51,11 +51,11 @@
Ur tesɛiḍ ara irewwayen.
Tella-d tucḍa.
- Tilγa
+ Tilɣa
D acu i ttummant\?
Ticraḍ
- Rnu γer ticraḍ
+ Rnu ɣer ticraḍ
Ticraḍ
Sgugem %s
Bder
@@ -64,20 +64,20 @@
Sefsex
Anasiw n imujiyen
Rnu iccer
- Nγel aseγwen
+ Nɣel aseɣwen
Ldi amzun d %s
Bḍu amzun d…
- Bḍu aseγwen n tijewwiq s…
+ Bḍu aseɣwen n tijewwiq s…
Bḍu tijewwiqt d…
Anta tummant\?
d-acu i gellan d amaynut\?
Nadi…
Tiririn…
- Tugna n umaγnu
+ Tugna n umaɣnu
Sider
Kkes tijewwiqt-a\?
- Ẓreg tilγa
+ Ẓreg tilɣa
Agrudem
Aceɛlal
Aberkan
@@ -88,7 +88,7 @@
%1$s, %2$s, akked %3$s
%1$s akked %2$s
Tusky %s
- Amaγnu n Tusky
+ Amaɣnu n Tusky
Tugniwin
Tibidyutin
@@ -110,16 +110,16 @@
Imiḍanen yettwasgugmen
Imiḍanen yettusḥebsen
- Tiγula yettwaffren
+ Tiɣula yettwaffren
Isuturen n teḍfeṛt
- Taγwalt
+ Taɣwalt
Sfeḍ
Imiḍanen yettwasgugmen
Imiḍanen yettusḥebsen
- Tiγula yettwaffren
+ Tiɣula yettwaffren
Isuturen n teḍfeṛt
- Ẓreg tilγa
- Taγwalt
+ Tilɣa
+ Taywalt
Kkes
Azen
@@ -129,27 +129,27 @@
Rnu assenqed
Ṭef tugna
Timeẓriwt n tijewwaqt
- Sγiwes tijewwaqt-a
+ Sɣiwes tijewwaqt-a
Bḍu agbur n tijewwiqt-a
- Bḍu aseγwen γer tijewwiqt
+ Bḍu aseɣwen ɣer tijewwiqt
Rnu amsizdeg
Ẓreg amsizdeg
- Snulfu-d umuγ
- Snifel isem n wumuγ
- Kkes umuγ-a
- Ẓreg umuγ-a
- Rnu yiwen umiḍan γer tabdert
- Kkes amiḍan seg wumuγ
+ Snulfu-d umuɣ
+ Snifel isem n wumuɣ
+ Kkes umuɣ-a
+ Ẓreg umuɣ-a
+ Rnu yiwen umiḍan ɣer wummuɣ
+ Kkes amiḍan seg wumuɣ
Rnu isefka
- Isem n wumuγ
+ Isem n wumuɣ
Fren tabdart
- Umuγ
+ Umuɣ
Sizdeg
Imiḍanen
Rnu yiwen wefran
- Ccetki γef @%s
+ Ccetki ɣef @%s
Ccetki
Ggami
Yessidired %1$s
@@ -159,16 +159,16 @@
itteqqen…
Issalay…
- fukken kran n wadγaren
+ fukken kran n wadɣaren
Imzizdigen
Akken yella yiṭij
Iminig
- Sken-ed tiririyin
+ Sken-d tiririyin
Apṛuksi HTTP
Tansa n upṛuksi HTTP
Imeḍfaṛen imaynuten
- Adγaren
+ Adɣaren
Yuder-ik-id %s
Yettwargel umiḍan
@@ -183,26 +183,26 @@
Ales tanekra
Tuccḍa n usider
- Igujj %1$s γer:
+ Igujj %1$s ɣer:
Kkes asenṭeḍ
%1$s
%1$s akked %2$s
%1$s, %2$s akked %3$d nniḍen
Aru tijewwiqt
- %1$s • %2$s
+ %1$s • %2$s
- - %s wedγar
- - %s n yedγaren
+ - %s n wedɣar
+ - %s n yedɣaren
%s id yugran
ad ifak deg %s
ifuk
- Dγer
+ Dɣer
- Ifuk, tura kan, yiwen wedγar t tteki-iḍ degs
- Ifukk yiwen wedγar id snulfaḍ
+ Ifuk, tura kan, yiwen wedɣar t tteki-iḍ degs
+ Ifukk yiwen wedɣar id snulfaḍ
- %d n wass
@@ -214,18 +214,18 @@
- %d n tasdidt
- - %d n tesdidin
+ - %d n tisdidin
Kemmel
- Uγal
+ Uɣal
Tella-d tuccḍa deg ccetki
Tucḍa n unadi
Assenqed
- 5 n tasditin
- 30 n tasditin
- 1 n wesrag
- 6 n wesragen
+ 5 n tisdidin
+ 30 n tisdidin
+ 1 n usrag
+ 6 n isragen
1 n wass
3 n wussan
7 n wussan
@@ -233,30 +233,66 @@
Ig ṭafaṛ
Imeḍfaṛen
- Nadi γef medden i teṭafareḍ
+ Nadi ɣef medden i teṭafareḍ
Imeḍfaṛen
- Iseγwan
+ Iseɣwan
Tibdarin
Tibdarin
- Iseγwan
+ Iseɣwan
Yettwaceyyaɛ!
Yettwaceyyaɛ!
Ula d yiwen n ugmuḍ
I yimeḍfaṛen kan
- Teγzi n weḍṛis
+ Teɣzi n weḍṛis
Yettwamdemmar s Tusky
Asmel Web n usenfaṛ:
\n https://tusky.app
- %dasr
- %dtas
- %dtasn
+ %dsr
+ %dtsd
+ %dtsn
Sekles amzun d arewway\?
Ticki
Aṛubut
- Yettwarna γer ticṛad
+ Yettwarna ɣer ticṛad
+ deg %dsr
+ deg %dtsd
+ deg %dtsn
+
+ - %d n tasint
+ - %d n tasinin
+
+
+ Agbur amḥulfu
+ Creḍ allal n teywalt amzun d amḥulfu
+ Wennez tikkelt-nniḍen
+ Asali ur yeddi ara.
+ Tuccḍa deg tuzna n tijewwiqt.
+
+ Adigan
+ Turagin
+
+ Yebḍa-t %s
+ Yebḍa %s tijewwiqt-ik·im
+ Yerna %s tijewwiqt-ik·im ɣer imenyafen-is
+ Tiririt taruradt
+ Bḍu
+ Kkes beṭu
+ Ffer beṭuyat
+ Sken-d beṭuyat
+ Ur sgugum ara
+ Ddeg
+ Ihacṭagen
+ Sken-d beṭuyat
+ Ihacṭagen
+ Aseqdac nni ur yettwasgugem ara tura
+ %s ur yettwaffer ara
+
+ Assisen
+ Tugna n yiɣef n umaɣnu
+
From 83e8c9eb7571638047de1b3e5e985e84f70dc195 Mon Sep 17 00:00:00 2001
From: Vegard Skjefstad
Date: Fri, 6 Mar 2020 11:27:21 +0000
Subject: [PATCH 14/42] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegian?=
=?UTF-8?q?=20Bokm=C3=A5l)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/nb_NO/
---
app/src/main/res/values-no-rNB/strings.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-no-rNB/strings.xml b/app/src/main/res/values-no-rNB/strings.xml
index aeb6c51f0..9f5ec3810 100644
--- a/app/src/main/res/values-no-rNB/strings.xml
+++ b/app/src/main/res/values-no-rNB/strings.xml
@@ -520,4 +520,6 @@
Du har ikke lagret noen kladder.
Lydfiler må være mindre enn 40MB.
-
+ Mastodon har et minimums planleggingsinterval på 5 minutter.
+
+
From 5471670129e7dfd55b61b22fbe16102b86f9cff4 Mon Sep 17 00:00:00 2001
From: knuxify
Date: Fri, 6 Mar 2020 11:27:21 +0000
Subject: [PATCH 15/42] Translated using Weblate (Polish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/pl/
---
app/src/main/res/values-pl/strings.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index d2c1acfd1..0a46fa5b4 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -498,4 +498,6 @@
Nie masz żadnych szkiców.
Nie masz żadnych zaplanowanych wpisów.
+Mastodon umożliwia wysłanie minimalnie 5 minut od zaplanowania.
+
From 38c2c24a09b43b502871dc33f94bf6ccb97fa729 Mon Sep 17 00:00:00 2001
From: tolstoevsky
Date: Fri, 6 Mar 2020 11:27:22 +0000
Subject: [PATCH 16/42] Translated using Weblate (Russian)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/ru/
---
app/src/main/res/values-ru/strings.xml | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index c63298079..f1638c57f 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -546,4 +546,13 @@
Добавлено в закладки
Выбрать список
Список
-
+ Аудиофайлы должны быть меньше 40МБ.
+ Показывать цветные градиенты для скрытых медиа
+
+ Ошибка поиска поста %s
+
+ У вас нет черновиков.
+ У вас нет запланированных постов.
+ Минимальный интервал планирования в Mastodon составляет 5 минут.
+
+
From 1710b980bc32a3ac4f274e26e36393ff187b86e7 Mon Sep 17 00:00:00 2001
From: Weblate
Date: Fri, 6 Mar 2020 11:27:22 +0000
Subject: [PATCH 17/42] Added translation using Weblate (English (Australia))
Translated using Weblate (Swedish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/sv/
Translated using Weblate (Japanese)
Currently translated at 87.1% (357 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/ja/
Translated using Weblate (Esperanto)
Currently translated at 99.8% (409 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/eo/
---
app/src/main/res/values-en-rAU/strings.xml | 2 ++
app/src/main/res/values-eo/strings.xml | 5 ++++-
app/src/main/res/values-ja/strings.xml | 6 ++++++
app/src/main/res/values-sv/strings.xml | 1 +
4 files changed, 13 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/res/values-en-rAU/strings.xml
diff --git a/app/src/main/res/values-en-rAU/strings.xml b/app/src/main/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000..a6b3daec9
--- /dev/null
+++ b/app/src/main/res/values-en-rAU/strings.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index 1779798d6..bd1672bc1 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -469,4 +469,7 @@
Listo
Eraro dum elserĉo de la mesaĝo %s
-
+Aŭdia dosiero devas esti malpli ol 40MB.
+ Montri buntajn transirojn por kaŝitaj aŭdovidaĵoj
+
+
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index dfcbca0a8..90f12f08e 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -409,4 +409,10 @@
通知フィルターを表示
+ リセット
+ 音声ファイルは40MB未満にしてください。
+ ブックマーク
+ ブックマーク
+ 編集
+ ブックマーク
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 4bad9f70f..4bb611c69 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -482,4 +482,5 @@
Ljudfiler måste vara mindre än 40MB.
Du har inga utkast.
+ Mastodon har ett minimalt schemaläggningsintervall på 5 minuter.
From 8ffb263e16a1c2d7a45df0e561320ee78d7afafe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A9lanie=20Chauvel?=
Date: Fri, 6 Mar 2020 11:27:22 +0000
Subject: [PATCH 18/42] Translated using Weblate (French)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/fr/
Translated using Weblate (Esperanto)
Currently translated at 99.8% (409 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/eo/
---
app/src/main/res/values-eo/strings.xml | 2 ++
app/src/main/res/values-fr/strings.xml | 34 +++++++++++++-------------
2 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index bd1672bc1..a8704d16b 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -472,4 +472,6 @@
Aŭdia dosiero devas esti malpli ol 40MB.
Montri buntajn transirojn por kaŝitaj aŭdovidaĵoj
+ Vi ne havas iun ajn malneton.
+ Vi ne havas iun ajn planitan mesaĝon.
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 36bef8c9e..8d7d69526 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -39,7 +39,7 @@
Brouillons
Licences
\@%s
- %s a boosté
+ %s a partagé
Contenu sensible
Média caché
Cliquer pour voir
@@ -49,15 +49,15 @@
Replier
Rien ici.
Il n’y a aucun pouet pour le moment.\nGlissez vers le bas pour actualiser !
- %s a boosté votre pouet
+ %s a partagé votre pouet
%s a ajouté votre pouet à ses favoris
%s vous suit
Signaler @%s
Commentaires additonnels ?
Réponse rapide
Répondre
- Booster
- Supprimer le boost
+ Partager
+ Annuler le partage
Favori
Supprimer le favori
Plus
@@ -69,8 +69,8 @@
Ne plus suivre
Bloquer
Débloquer
- Cacher les boosts
- Montrer les boosts
+ Cacher les partages
+ Montrer les partages
Signaler
Supprimer
POUET
@@ -109,8 +109,8 @@
Liens
Mentions
Hashtags
- Afficher l’auteur·rice du boost
- Afficher les boosts
+ Afficher l’auteur·rice du partage
+ Montrer les partages
Montrer les favoris
Hashtags
Mentions
@@ -170,7 +170,7 @@
Me notifier lorsque
on me mentionne
on me suit
- mes messages sont boostés
+ mes pouets sont partagés
mes messages sont mis en favoris
Apparence
Thème de l’application
@@ -187,7 +187,7 @@
Langue
Filtrage des fils
Onglets
- Afficher les boosts
+ Montrer les partages
Afficher les réponses
Montrer les miniatures des médias
Proxy
@@ -213,7 +213,7 @@
Nouveaux abonnés
Notifications pour les nouveaux abonnés
Partages
- Notifications quand vos pouets sont boostés
+ Notifications quand vos pouets sont partagés
Favoris
Notifications quand vos pouets sont mis en favoris
%s vous a mentionné
@@ -316,8 +316,8 @@
Échec du téléchargement
Robot
%1$s a déménagé vers :
- Booster vers l’audience originale
- Ne plus booster
+ Partager à l’audience originale
+ Annuler le partage
Tusky contient du code et des ressources issus des projets open source suivants :
Sous licence Apache (copie ci-dessous)
CC-BY 4.0
@@ -335,10 +335,10 @@
- %1$s Favoris
- - <b>%s</b> Boost
- - <b>%s</b> Boosts
-
- Boosté par
+ - %s Partage
+ - %s Partages
+
+ Partagé par
Mis en favoris par
%1$s
%1$s et %2$s
From d974f474088f09bc3f6c5f75107a93fbad289bea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?=
Date: Fri, 6 Mar 2020 11:27:22 +0000
Subject: [PATCH 19/42] Translated using Weblate (Icelandic)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/is/
---
app/src/main/res/values-is/strings.xml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml
index 12e3dac2d..4bbd56dc8 100644
--- a/app/src/main/res/values-is/strings.xml
+++ b/app/src/main/res/values-is/strings.xml
@@ -508,4 +508,6 @@
Þú ert ekki með neinar áætlaðar stöðufærslur.
Hljóðskrár verða að vera minni en 40MB.
-
+ Mastodon er með 5 mínútna lágmarksbil fyrir áætlaðar aðgerðir.
+
+
From 91dd3e4f5f47d10b4647c9599f965e23be91a0bc Mon Sep 17 00:00:00 2001
From: Daniele Lira Mereb
Date: Fri, 6 Mar 2020 11:27:22 +0000
Subject: [PATCH 20/42] Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/pt_BR/
---
app/src/main/res/values-pt-rBR/strings.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 41370ef78..781895312 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -486,4 +486,5 @@
Áudios devem ser menores que 40MB.
Sem rascunhos.
+ Mastodon possui um intervalo mínimo de 5 minutos para agendar.
From 6a87e3e35295a2b14086b737741f8ae0b80b0462 Mon Sep 17 00:00:00 2001
From: Ivan Kupalov
Date: Mon, 9 Mar 2020 20:43:01 +0100
Subject: [PATCH 21/42] Work around the bug in ComposeScheduleView, fix #1720
(#1722)
DatePicker seems to think that it's in UTC. So setting selected time
might not work as aspect and receiving value from it might be in UTC
as well. This commit fixes the second issue by interpreting the date
as UTC date. Tested with America/New_York (GMT-5 at the moment) and
Russia/Kamchatka (GMT+12).
---
.../tusky/components/compose/view/ComposeScheduleView.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.java b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.java
index 0deb20be7..ca6639369 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.java
+++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeScheduleView.java
@@ -192,6 +192,9 @@ public class ComposeScheduleView extends ConstraintLayout {
private void onDateSet(long selection) {
initializeSuggestedTime();
Calendar newDate = getCalendar();
+ // working around bug in DatePicker where date is UTC #1720
+ // see https://github.com/material-components/material-components-android/issues/882
+ newDate.setTimeZone(TimeZone.getTimeZone("UTC"));
newDate.setTimeInMillis(selection);
scheduleDateTime.set(newDate.get(Calendar.YEAR), newDate.get(Calendar.MONTH), newDate.get(Calendar.DATE));
openPickTimeDialog();
From f67d6b4f7f298075ed5290e623ec506b84af37ff Mon Sep 17 00:00:00 2001
From: ButterflyOfFire
Date: Sun, 15 Mar 2020 21:14:00 +0000
Subject: [PATCH 22/42] Translated using Weblate (Kabyle)
Currently translated at 59.5% (244 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (Kabyle)
Currently translated at 59.5% (244 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/kab/
Translated using Weblate (Chinese (Traditional))
Currently translated at 88.5% (363 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/zh_Hant/
Translated using Weblate (Chinese (Hong Kong))
Currently translated at 82.7% (339 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/zh_Hant_HK/
Translated using Weblate (Chinese)
Currently translated at 82.7% (339 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/zh_MO/
Translated using Weblate (Arabic)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/ar/
---
app/src/main/res/values-ar/strings.xml | 110 ++++++++++-----------
app/src/main/res/values-kab/strings.xml | 14 ++-
app/src/main/res/values-zh-rHK/strings.xml | 2 +-
app/src/main/res/values-zh-rMO/strings.xml | 2 +-
app/src/main/res/values-zh-rTW/strings.xml | 2 +-
5 files changed, 67 insertions(+), 63 deletions(-)
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index c40e7585c..b54b278a9 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -2,82 +2,82 @@
وقع هناك خطأ.
حدث خطأ في الشبكة! يرجى التحقق من اتصالك ثم أعد المحاولة!
- لا يجب أن يترك فارغا.
- اسم النطاق غير صالح
+ لا يجب أن يترك هذا الحقل فارغا.
+ اسم النطاق الذي قمتَ بإدخاله غير صالح
اخفقت المصادقة مع مثيل الخادم هذا.
- تعذر العثور على متصفح ويب قابل للإستعمال.
+ تعذر العثور على متصفح ويب صالح للإستعمال.
لقد وقع هناك خطأ مجهول في التصريح.
تم رفض التصريح.
- فشل الحصول على رمز الدخول.
- المنشور طويل جدا !
- يجب أن يكون حجم الملف أقل من 4 ميغابايت.
+ فشل الحصول على رمز الولوج.
+ إنّ المنشور طويل جدا!
+ يجب أن يكون حجم الملف أقل من 8 ميغابايت.
يجب أن يكون حجم ملفات الفيديو أقل من 40 ميغا بايت.
- لا يمكن رفع هذا النوع من الملفات.
+ لا يمكن تحميل هذا النوع من الملفات.
تعذر فتح ذاك الملف.
التصريح لازم لقراءة الوسائط.
التصريح لازم للإحتفاظ بالوسائط.
- لا يمكنك إرفاق كلا من الصور و الفيديوهات في نفس المنشور.
+ لا يمكنك إرفاق كلا من الصور والفيديوهات في نفس المنشور في آن واحد.
اخفقت عملية الرفع.
خطأ عند إرسال التبويق.
- الرئيسية
+ الرئيسي
الاشعارات
- المحلية
- الفدرالية
+ المحلي
+ الفدرالي
الرسائل المباشرة
الألسنة
تبويق
- المشاركات
- يحتوي على ردود
- مدبّس
+ المنشورات
+ التبويقات والردود
+ المدبّسة
المتابَعون
المتابِعون
المفضلة
الحسابات المكتومة
الحسابات المحظورة
طلبات المتابعة
- عدل ملفك الشخصي
+ عدل ملفك التعريفي
المسودات
الرّخص
\@%s
- %s رقّي
+ شارَكَه %s
محتوى حساس
وسائط مخفية
اضغط للعرض
- اعرض أكثر
+ اعرض المزيد
اعرض أقل
توسيع
تصغير
لا شيء هنا.
- لا يوجد شيئ هنا. إسحب إلى أسفل للتحديث !
- رقّى %s تبويقك
+ لا يوجد شيء هنا. إسحب إلى أسفل للإنعاش!
+ شارَك %s تبويقك
أعجِب %s بتبويقك
%s يتبعك
أبلغ عن @%s
- تعليقات إضافية ؟
- إجابة سريعة
- أجب
+ تعليقات إضافية؟
+ رد سريع
+ رد
رقّي
إزالة الترقية
تفضيل
إزالة المفضلة
المزيد
حرر
- التسجيل بواسطة ماستدون
+ الولوج إلى ماستدون
خروج
- متأكد مِن أنك تود الخروج من الحساب %1$s ؟
+ متأكد مِن أنك تود الخروج من الحساب %1$s؟
إتبع
- إلغاء التتبع
+ إلغاء المتابعة
قم بحظره
إلغاء الحظر
إخفاء الترقيات
إظهار الترقيات
- أبلغ
+ أبلغ عنه
إحذف
بَوّق
بوّق!
- إعادة المحاولة
+ أعد المحاولة
إغلاق
- الملف الشخصي
+ الملف التعريفي
التفضيلات
تفضيلات الحساب
المفضلة
@@ -95,7 +95,7 @@
إخفاء الوسائط
إفتح الدرج
إحفظ
- تعديل الملف الشخصي
+ تعديل الملف التعريفي
تعديل
إلغاء
موافقة
@@ -109,8 +109,8 @@
الروابط
الإشارات
الوسوم
- عرض الترقيات
- عرض المفضلات
+ اعرض الترقيات
+ اعرض المفضلات
الوسوم
الإشارات
الروابط
@@ -120,23 +120,23 @@
شاركه كـ…
شارك رابط التبويق مع…
شارك التبويق على…
- شارك رابط التبويق مع…
- تم الإرسال !
+ شارك الوسيط مع…
+ تم إرساله!
تم فك الحجب عن الحساب
- تم فك الكتم عن الحساب
- تم إرساله !
+ لم يعد الحساب مكتومًا
+ تم إرساله!
تم إرسال الرد بنجاح.
- أي سيرفر ؟
- ما الجديد ؟
+ أي مثيل خادم؟
+ ما الجديد؟
تحذير عن المحتوى
الإسم العلني
السيرة
البحث عن…
- لم يتم العثور على نتائج
- إجابة …
- الصورة الرمزية
- رأس الصفحة
- ماذا نعني بمثيل الخادم ؟
+ لم يتم العثور على أية نتائج
+ رد…
+ صورة الملف التعريفي
+ صورة رأس الصفحة
+ ماذا نعني بمثيل الخادم؟
الإتصال جارٍ…
بإمكانك إدخال عنوان أي مثيل خادوم ماستدون هنا. على سبيل المثال mastodon.social أو icosahedron.website أو social.tchncs.de أوالإطلاع على لاكتشاف المزيد !
\n
@@ -148,13 +148,13 @@
تتمة رفع الوسائط
الإرسال جارٍ…
تنزيل
- هل تريد رفض طلب المتابعة ؟
- هل تود إلغاء متابعة هذا الحساب ؟
+ هل تريد رفض طلب المتابعة؟
+ هل تود إلغاء متابعة هذا الحساب؟
هل تريد حذف هذا التبويق؟
- عمومي : ينشر على الخيوط العمومية
- غير مدرج : لا يُعرَض على الخيوط العمومية
- لمتابعيك فقط : يُنشر إلى متابعيك فقط
- مباشر : يُنشر إلى المستخدمين المشار إليهم فقط
+ للعامة: ينشر على الخيوط العمومية
+ غير مدرج: لا يُعرَض على الخيوط العمومية
+ لمتابعيك فقط: يُنشر إلى متابعيك فقط
+ مباشر: يُنشر إلى المستخدمين المشار إليهم فقط
تعديل الاشعارات
الإخطارات
التنبيهات
@@ -163,12 +163,12 @@
إعلام بالضوء
أخطرني عندما
يشار إلي
- يتبعني أحد
+ يتبعني أحدهم
تُرقّى منشوراتي
- أعجب أحد ما بمنشوراتي
+ يُعجَب أحد ما بمنشوراتي
المظهر
- سمة التطبيق
- الخيوط
+ حُلّة التطبيق
+ الخيوط الزمنية
عوامل التصفية
داكنة
فاتحة
@@ -339,7 +339,7 @@
إظهار صاحب الترقية
افتح الوسيط #%d
- تنزيل الوسائط
+ نزّل الوسائط
جارٍ تنزيل الوسائط
هل تريد حذف وإعادة صياغة هذا التبويق؟
@@ -378,7 +378,7 @@
لقد انتهى استطلاع رأي قمتَ بإنشائه
-انتهت استطلاعات الرأي
+تنتهي استطلاعات الرأي
- %1$s" مفضلة"
- %1$s مفضلة
@@ -457,7 +457,7 @@
الكلمة كاملة
استطلاع رأي بالخيارات: %1$s, %2$s, %3$s, %4$s; %5$s
- هل أنت متأكد من أنك تريد حجب كافة %s ؟ سوف لن يكون باستطاعتك رؤية أي محتوى قادم من هذا النطاق بعد الآن ، لا في الخيوط الزمنية العامة ولا في إخطاراتك. سيتم إزالة متابِعيك الذين هم على هذا النطاق.
+ هل أنت متأكد من أنك تريد حجب كافة %s؟ سوف لن يكون باستطاعتك رؤية أي محتوى قادم من هذا النطاق بعد الآن ، لا في الخيوط الزمنية العامة ولا في إخطاراتك. سيتم إزالة متابِعيك الذين هم على هذا النطاق.
سيتم إرسال التقرير إلى مشرفي خادمك. يمكنك تقديم تفسير عن سبب الإبلاغ عن الحساب أدناه:
هذا الحساب ينتسب إلى خادم آخر. هل تريد إرسال نسخة مجهولة من التقرير إلى هناك أيضا؟
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index b91bee1ed..07b6d44e8 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -109,13 +109,13 @@
Senṭeḍ
Imiḍanen yettwasgugmen
- Imiḍanen yettusḥebsen
+ Imiḍan yettwacekklen
Tiɣula yettwaffren
Isuturen n teḍfeṛt
Taɣwalt
Sfeḍ
Imiḍanen yettwasgugmen
- Imiḍanen yettusḥebsen
+ Imiḍanen yettwacekklen
Tiɣula yettwaffren
Isuturen n teḍfeṛt
Tilɣa
@@ -150,7 +150,7 @@
Imiḍanen
Rnu yiwen wefran
Ccetki ɣef @%s
- Ccetki
+ Ccetki fell-as
Ggami
Yessidired %1$s
@@ -277,8 +277,8 @@
Turagin
Yebḍa-t %s
- Yebḍa %s tijewwiqt-ik·im
- Yerna %s tijewwiqt-ik·im ɣer imenyafen-is
+ %s Y·Tebḍa tijewwiqt-ik·im
+ %s Y·Terna tijewwiqt-ik·im ɣer imenyafen-is
Tiririt taruradt
Bḍu
Kkes beṭu
@@ -295,4 +295,8 @@
Assisen
Tugna n yiɣef n umaɣnu
+ Ur ilaq ara ad yili d ilem.
+ Cekkel
+ Kkes tacekkalt
+ Tettwakkes tacekkalt ɣef umiḍan-nni
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index 3311a2bbb..b945f5145 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -161,7 +161,7 @@
正在連線…
- 請輸入你帳號所在的 Mastodon 站點的域名或地址
+ "請輸入你帳號所在的 Mastodon 站點的域名或地址 "
diff --git a/app/src/main/res/values-zh-rMO/strings.xml b/app/src/main/res/values-zh-rMO/strings.xml
index 3311a2bbb..b945f5145 100644
--- a/app/src/main/res/values-zh-rMO/strings.xml
+++ b/app/src/main/res/values-zh-rMO/strings.xml
@@ -161,7 +161,7 @@
正在連線…
- 請輸入你帳號所在的 Mastodon 站點的域名或地址
+ "請輸入你帳號所在的 Mastodon 站點的域名或地址 "
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index a41e19148..506c24eca 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -161,7 +161,7 @@
正在連線…
- 請輸入你帳號所在的 Mastodon 站點的域名或地址
+ "請輸入你帳號所在的 Mastodon 站點的域名或地址 "
正在完成上傳…
From 62dbb752008cde1dfafcb5e770ed7ad9a659aae9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Isak=20Holmstr=C3=B6m?=
Date: Sun, 15 Mar 2020 21:14:00 +0000
Subject: [PATCH 23/42] Translated using Weblate (Swedish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/sv/
Translated using Weblate (Swedish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/sv/
Translated using Weblate (Swedish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/sv/
---
app/src/main/res/values-sv/strings.xml | 42 +++++++++++++-------------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 4bb611c69..f23ca5b12 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -20,7 +20,7 @@
Uppladdningen misslyckades.
Kunde inte skicka toot.
Hem
- Notifikationer
+ Aviseringar
Lokalt
Federerat
Direkta meddelanden
@@ -158,15 +158,15 @@
Olistad: Visa inte i offentliga tidslinjer
Enbart-följare: Ses enbart av följare
Direkt: Skicka endast till nämnda användare
- Notifikationer
- Notifikationer
+ Aviseringar
+ Aviseringar
Alarm
Meddela med ljud
Meddela med vibration
Notifieringar med LED
Meddela mig när
omnämnd
- följande
+ nya följare
mina inlägg är knuffade
mina inlägg är favoriserade
Utseende
@@ -206,13 +206,13 @@
Stor
Största
Nya omnämnanden
- Notifieringar om nya omnämnanden
+ Aviseringar om nya omnämnanden
Nya följare
- Notifieringar om nya följare
+ Aviseringar på nya följare
Knuffar
- Notifieringar när dina toots blir knuffade
+ Aviseringar när dina toots blir knuffade
Favoriter
- Notifieringar när dina toots blir markerade som favoriter
+ Aviseringar när dina toots blir markerade som favoriter
%s omnämnde dig
%1$s, %2$s, %3$s och %4$d andra
%1$s, %2$s, och %3$s
@@ -239,7 +239,7 @@
Dela länk till toot
Bilder
Video
- Följarförfrågad
+ Följarförfrågan
om %dy
om %dd
@@ -272,9 +272,9 @@
Kunde inte byta namn på lista
Kunde inte radera lista
Skapa en lista
- Byt namn
- Ta bort
- Ändra
+ Byt namn på listan
+ Ta bort listan
+ Redigera lista
Sök efter personer du följer
Lägg till konto i listan
Ta bort kontot från listan
@@ -305,7 +305,7 @@
Senare
Starta om
Standard-emojis för din enhet
- Emojis baserade på The Blob emojis kända från Android 4.4–7.1
+ Emojis baserade på The Blob emojis från Android 4.4–7.1
Mastodon\'s standard emojis
Nedladdning misslyckad
Robot
@@ -373,9 +373,9 @@
Visa robotindikator
- Är du säker på att du vill rensa dina notifieringar permanent\?
+ Är du säker på att du vill rensa dina aviseringar permanent\?
-Radera och skriv nytt
+Radera och skriv på nytt
Radera och skriv ny toot\?
%1$s • %2$s
@@ -384,15 +384,15 @@
- %s röster
%s kvar
- avslutas %s
+ avslutas vid %s
stängd
Rösta
-omröstning är avslutad
+omröstningar har avslutats
Omröstningar
- Notifieringar när omröstningar har avslutats
+ Aviseringar när omröstningar har avslutats
En omröstning där du har röstat är avslutad
@@ -424,9 +424,9 @@
Dolda domäner
Dolda domäner
Tysta %s
- %s inte tystnad längre
+ %s inte tystad längre
- Är du säker på att du vill blockera allt från %s\? Du kommer inte kunna se något innehåll från denna domän i publika tidslinje eller i dina notifieringar. Dina följare på domänen kommer inte att bli borttagna.
+ Är du säker på att du vill blockera allt från %s\? Du kommer inte kunna se något innehåll från denna domän i publika tidslinjer eller i dina notifieringar. Dina följare på domänen kommer inte att bli borttagna.
Dölj hela domänen
Google\'s nuvarande emojis
@@ -443,7 +443,7 @@
Visa notifikationsfilter
Helt ord
- När nyckelordet eller frasen är alfanumerisk enbart, blir den enbart appliceras om den matchar hela ordet
+ När nyckelordet eller frasen enbart är alfanumerisk, appliceras den om den matchar hela ordet
Expandera alltid toots med innehållsvarningar
Konton
Sökning misslyckades
From 06eb308e93c23b76f5cd5de877688a9687d796c6 Mon Sep 17 00:00:00 2001
From: hg
Date: Sun, 15 Mar 2020 21:14:00 +0000
Subject: [PATCH 24/42] Translated using Weblate (Swedish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/sv/
Translated using Weblate (Swedish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/sv/
---
app/src/main/res/values-sv/strings.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index f23ca5b12..53f7308f0 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -177,7 +177,7 @@
Ljust
Svart
Automatiskt vid solnedgång
- Använd systemdesign
+ Använd system-tema
Webbläsare
Använd Chrome-anpassade flikar
Dölj skriv-knappen vid skrollning
@@ -287,7 +287,7 @@
Kräver att du manuellt godkänner följare
Spara utkast?
Skickar toot…
- Fel vid sändning av toot
+ Kunde inte skicka toot
Skickar toot
Sändning avbruten
En kopia av tooten har sparats i dina utkast
@@ -298,7 +298,7 @@
Systemstandard
Du behöver ladda ned dessa emojis först
Utför sökning…
- Expandera/Dölj alla status
+ Expandera/Dölj alla statusar
Öppna toot
Omstart av appen krävs
Du måste starta om Tusky för att tillämpa ändringarna
From 9c4898117e0d7a546d43d5ee92a7367d3ac337be Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Tue, 17 Mar 2020 19:46:32 +0100
Subject: [PATCH 25/42] Ensure media are marked sensitive any time there's a
content warning (#1728)
Addresses the most critical part of #1725, and may also apply to #1721
---
.../keylesspalace/tusky/components/compose/ComposeViewModel.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt
index b8ade248a..fa6647984 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt
@@ -249,7 +249,7 @@ class ComposeViewModel
content,
spoilerText,
statusVisibility.value!!.serverString(),
- mediaUris.isNotEmpty() && markMediaAsSensitive.value!!,
+ mediaUris.isNotEmpty() && (markMediaAsSensitive.value!! || showContentWarning.value!!),
mediaIds,
mediaUris.map { it.toString() },
mediaDescriptions,
From 43162789c1658249b27c1004ee9c1d7f4d62a26c Mon Sep 17 00:00:00 2001
From: Konrad Pozniak
Date: Wed, 18 Mar 2020 17:12:56 +0100
Subject: [PATCH 26/42] upgrade Kotlin to 1.3.70 (#1730)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index a71af6f14..38f3d2b41 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.3.61'
+ ext.kotlin_version = '1.3.70'
repositories {
google()
jcenter()
From 4a4dd4f30f7c3f6124795b4cb905fc2bae6ee4a0 Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Thu, 19 Mar 2020 22:02:10 +0100
Subject: [PATCH 27/42] Add notifications for follow requests (#1729)
* Add notifications for follow requests
Issue #1719
* Revert item_follow_request layout, create new layout for follow request notifications
* Migrate follow request interaction from notification to observable pattern
* Filter follow request notifications by default
* Add missing cases for system notification generation
* Format code
---
.../22.json | 735 ++++++++++++++++++
.../tusky/adapter/FollowRequestViewHolder.kt | 51 ++
.../tusky/adapter/FollowRequestsAdapter.java | 60 +-
.../tusky/adapter/NotificationsAdapter.java | 26 +-
.../keylesspalace/tusky/db/AccountEntity.kt | 3 +-
.../keylesspalace/tusky/db/AppDatabase.java | 11 +-
.../com/keylesspalace/tusky/di/AppModule.kt | 2 +-
.../tusky/entity/Notification.kt | 3 +-
.../tusky/fragment/NotificationsFragment.java | 31 +-
.../NotificationPreferencesFragment.kt | 54 +-
.../tusky/network/MastodonApi.kt | 10 +
.../tusky/util/NotificationHelper.java | 14 +-
.../item_follow_request_notification.xml | 96 +++
app/src/main/res/values/strings.xml | 4 +
.../main/res/xml/notification_preferences.xml | 6 +
.../tusky/ComposeActivityTest.kt | 1 +
16 files changed, 1003 insertions(+), 104 deletions(-)
create mode 100644 app/schemas/com.keylesspalace.tusky.db.AppDatabase/22.json
create mode 100644 app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
create mode 100644 app/src/main/res/layout/item_follow_request_notification.xml
diff --git a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/22.json b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/22.json
new file mode 100644
index 000000000..f5508a8d4
--- /dev/null
+++ b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/22.json
@@ -0,0 +1,735 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 22,
+ "identityHash": "eaa3c4d012fe743948343983fe1ae493",
+ "entities": [
+ {
+ "tableName": "TootEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `text` TEXT, `urls` TEXT, `descriptions` TEXT, `contentWarning` TEXT, `inReplyToId` TEXT, `inReplyToText` TEXT, `inReplyToUsername` TEXT, `visibility` INTEGER, `poll` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "urls",
+ "columnName": "urls",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "descriptions",
+ "columnName": "descriptions",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentWarning",
+ "columnName": "contentWarning",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToId",
+ "columnName": "inReplyToId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToText",
+ "columnName": "inReplyToText",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToUsername",
+ "columnName": "inReplyToUsername",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "visibility",
+ "columnName": "visibility",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "poll",
+ "columnName": "poll",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AccountEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `domain` TEXT NOT NULL, `accessToken` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `accountId` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `profilePictureUrl` TEXT NOT NULL, `notificationsEnabled` INTEGER NOT NULL, `notificationsMentioned` INTEGER NOT NULL, `notificationsFollowed` INTEGER NOT NULL, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `alwaysShowSensitiveMedia` INTEGER NOT NULL, `alwaysOpenSpoiler` INTEGER NOT NULL, `mediaPreviewEnabled` INTEGER NOT NULL, `lastNotificationId` TEXT NOT NULL, `activeNotifications` TEXT NOT NULL, `emojis` TEXT NOT NULL, `tabPreferences` TEXT NOT NULL, `notificationsFilter` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "domain",
+ "columnName": "domain",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accessToken",
+ "columnName": "accessToken",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isActive",
+ "columnName": "isActive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "displayName",
+ "columnName": "displayName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "profilePictureUrl",
+ "columnName": "profilePictureUrl",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsEnabled",
+ "columnName": "notificationsEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsMentioned",
+ "columnName": "notificationsMentioned",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFollowed",
+ "columnName": "notificationsFollowed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFollowRequested",
+ "columnName": "notificationsFollowRequested",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsReblogged",
+ "columnName": "notificationsReblogged",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFavorited",
+ "columnName": "notificationsFavorited",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsPolls",
+ "columnName": "notificationsPolls",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationSound",
+ "columnName": "notificationSound",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationVibration",
+ "columnName": "notificationVibration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationLight",
+ "columnName": "notificationLight",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "defaultPostPrivacy",
+ "columnName": "defaultPostPrivacy",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "defaultMediaSensitivity",
+ "columnName": "defaultMediaSensitivity",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "alwaysShowSensitiveMedia",
+ "columnName": "alwaysShowSensitiveMedia",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "alwaysOpenSpoiler",
+ "columnName": "alwaysOpenSpoiler",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mediaPreviewEnabled",
+ "columnName": "mediaPreviewEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastNotificationId",
+ "columnName": "lastNotificationId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "activeNotifications",
+ "columnName": "activeNotifications",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojis",
+ "columnName": "emojis",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tabPreferences",
+ "columnName": "tabPreferences",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFilter",
+ "columnName": "notificationsFilter",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_AccountEntity_domain_accountId",
+ "unique": true,
+ "columnNames": [
+ "domain",
+ "accountId"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_AccountEntity_domain_accountId` ON `${TABLE_NAME}` (`domain`, `accountId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "InstanceEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`instance` TEXT NOT NULL, `emojiList` TEXT, `maximumTootCharacters` INTEGER, `maxPollOptions` INTEGER, `maxPollOptionLength` INTEGER, `version` TEXT, PRIMARY KEY(`instance`))",
+ "fields": [
+ {
+ "fieldPath": "instance",
+ "columnName": "instance",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojiList",
+ "columnName": "emojiList",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "maximumTootCharacters",
+ "columnName": "maximumTootCharacters",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "maxPollOptions",
+ "columnName": "maxPollOptions",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "maxPollOptionLength",
+ "columnName": "maxPollOptionLength",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "version",
+ "columnName": "version",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "instance"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimelineStatusEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT, `visibility` INTEGER, `attachments` TEXT, `mentions` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "serverId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timelineUserId",
+ "columnName": "timelineUserId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "authorServerId",
+ "columnName": "authorServerId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToId",
+ "columnName": "inReplyToId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToAccountId",
+ "columnName": "inReplyToAccountId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojis",
+ "columnName": "emojis",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "reblogsCount",
+ "columnName": "reblogsCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "favouritesCount",
+ "columnName": "favouritesCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reblogged",
+ "columnName": "reblogged",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "bookmarked",
+ "columnName": "bookmarked",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "favourited",
+ "columnName": "favourited",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sensitive",
+ "columnName": "sensitive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "spoilerText",
+ "columnName": "spoilerText",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "visibility",
+ "columnName": "visibility",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "mentions",
+ "columnName": "mentions",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "application",
+ "columnName": "application",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "reblogServerId",
+ "columnName": "reblogServerId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "reblogAccountId",
+ "columnName": "reblogAccountId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "poll",
+ "columnName": "poll",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "serverId",
+ "timelineUserId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_TimelineStatusEntity_authorServerId_timelineUserId",
+ "unique": false,
+ "columnNames": [
+ "authorServerId",
+ "timelineUserId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_TimelineStatusEntity_authorServerId_timelineUserId` ON `${TABLE_NAME}` (`authorServerId`, `timelineUserId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "TimelineAccountEntity",
+ "onDelete": "NO ACTION",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "authorServerId",
+ "timelineUserId"
+ ],
+ "referencedColumns": [
+ "serverId",
+ "timelineUserId"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "TimelineAccountEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `timelineUserId` INTEGER NOT NULL, `localUsername` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `url` TEXT NOT NULL, `avatar` TEXT NOT NULL, `emojis` TEXT NOT NULL, `bot` INTEGER NOT NULL, PRIMARY KEY(`serverId`, `timelineUserId`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "serverId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timelineUserId",
+ "columnName": "timelineUserId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localUsername",
+ "columnName": "localUsername",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "displayName",
+ "columnName": "displayName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatar",
+ "columnName": "avatar",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojis",
+ "columnName": "emojis",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "bot",
+ "columnName": "bot",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "serverId",
+ "timelineUserId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ConversationEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `id` TEXT NOT NULL, `accounts` TEXT NOT NULL, `unread` INTEGER NOT NULL, `s_id` TEXT NOT NULL, `s_url` TEXT, `s_inReplyToId` TEXT, `s_inReplyToAccountId` TEXT, `s_account` TEXT NOT NULL, `s_content` TEXT NOT NULL, `s_createdAt` INTEGER NOT NULL, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_favourited` INTEGER NOT NULL, `s_bookmarked` INTEGER NOT NULL, `s_sensitive` INTEGER NOT NULL, `s_spoilerText` TEXT NOT NULL, `s_attachments` TEXT NOT NULL, `s_mentions` TEXT NOT NULL, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsible` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_poll` TEXT, PRIMARY KEY(`id`, `accountId`))",
+ "fields": [
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accounts",
+ "columnName": "accounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.id",
+ "columnName": "s_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.url",
+ "columnName": "s_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastStatus.inReplyToId",
+ "columnName": "s_inReplyToId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastStatus.inReplyToAccountId",
+ "columnName": "s_inReplyToAccountId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastStatus.account",
+ "columnName": "s_account",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.content",
+ "columnName": "s_content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.createdAt",
+ "columnName": "s_createdAt",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.emojis",
+ "columnName": "s_emojis",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.favouritesCount",
+ "columnName": "s_favouritesCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.favourited",
+ "columnName": "s_favourited",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.bookmarked",
+ "columnName": "s_bookmarked",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.sensitive",
+ "columnName": "s_sensitive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.spoilerText",
+ "columnName": "s_spoilerText",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.attachments",
+ "columnName": "s_attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.mentions",
+ "columnName": "s_mentions",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.showingHiddenContent",
+ "columnName": "s_showingHiddenContent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.expanded",
+ "columnName": "s_expanded",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.collapsible",
+ "columnName": "s_collapsible",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.collapsed",
+ "columnName": "s_collapsed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.poll",
+ "columnName": "s_poll",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id",
+ "accountId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'eaa3c4d012fe743948343983fe1ae493')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
new file mode 100644
index 000000000..d9fd0491e
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
@@ -0,0 +1,51 @@
+package com.keylesspalace.tusky.adapter
+
+import android.view.View
+import androidx.core.text.BidiFormatter
+import androidx.preference.PreferenceManager
+import androidx.recyclerview.widget.RecyclerView
+import com.keylesspalace.tusky.R
+import com.keylesspalace.tusky.entity.Account
+import com.keylesspalace.tusky.interfaces.AccountActionListener
+import com.keylesspalace.tusky.util.CustomEmojiHelper
+import com.keylesspalace.tusky.util.loadAvatar
+import com.keylesspalace.tusky.util.visible
+import kotlinx.android.synthetic.main.item_follow_request_notification.view.*
+
+internal class FollowRequestViewHolder(itemView: View, private val showHeader: Boolean) : RecyclerView.ViewHolder(itemView) {
+ private var id: String? = null
+ private val animateAvatar: Boolean = PreferenceManager.getDefaultSharedPreferences(itemView.context)
+ .getBoolean("animateGifAvatars", false)
+
+ fun setupWithAccount(account: Account, formatter: BidiFormatter?) {
+ id = account.id
+ val wrappedName = formatter?.unicodeWrap(account.name) ?: account.name
+ val emojifiedName: CharSequence = CustomEmojiHelper.emojifyString(wrappedName, account.emojis, itemView)
+ itemView.displayNameTextView.text = emojifiedName
+ if (showHeader) {
+ itemView.notificationTextView.text = itemView.context.getString(R.string.notification_follow_request_format, emojifiedName)
+ }
+ itemView.notificationTextView.visible(showHeader)
+ val format = itemView.context.getString(R.string.status_username_format)
+ val formattedUsername = String.format(format, account.username)
+ itemView.usernameTextView.text = formattedUsername
+ val avatarRadius = itemView.avatar.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_48dp)
+ loadAvatar(account.avatar, itemView.avatar, avatarRadius, animateAvatar)
+ }
+
+ fun setupActionListener(listener: AccountActionListener) {
+ itemView.acceptButton.setOnClickListener {
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ listener.onRespondToFollowRequest(true, id, position)
+ }
+ }
+ itemView.rejectButton.setOnClickListener {
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ listener.onRespondToFollowRequest(false, id, position)
+ }
+ }
+ itemView.avatar.setOnClickListener { listener.onViewAccount(id) }
+ }
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java
index 37f09f019..548d893c5 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestsAdapter.java
@@ -18,19 +18,12 @@ package com.keylesspalace.tusky.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.TextView;
import androidx.annotation.NonNull;
-import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView;
import com.keylesspalace.tusky.R;
-import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.interfaces.AccountActionListener;
-import com.keylesspalace.tusky.util.CustomEmojiHelper;
-import com.keylesspalace.tusky.util.ImageLoadingHelper;
public class FollowRequestsAdapter extends AccountAdapter {
@@ -46,7 +39,7 @@ public class FollowRequestsAdapter extends AccountAdapter {
case VIEW_TYPE_ACCOUNT: {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_follow_request, parent, false);
- return new FollowRequestViewHolder(view);
+ return new FollowRequestViewHolder(view, false);
}
case VIEW_TYPE_FOOTER: {
View view = LayoutInflater.from(parent.getContext())
@@ -60,57 +53,8 @@ public class FollowRequestsAdapter extends AccountAdapter {
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
if (getItemViewType(position) == VIEW_TYPE_ACCOUNT) {
FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
- holder.setupWithAccount(accountList.get(position));
+ holder.setupWithAccount(accountList.get(position), null);
holder.setupActionListener(accountActionListener);
}
}
-
- static class FollowRequestViewHolder extends RecyclerView.ViewHolder {
- private ImageView avatar;
- private TextView username;
- private TextView displayName;
- private ImageButton accept;
- private ImageButton reject;
- private String id;
- private boolean animateAvatar;
-
- FollowRequestViewHolder(View itemView) {
- super(itemView);
- avatar = itemView.findViewById(R.id.avatar);
- username = itemView.findViewById(R.id.usernameTextView);
- displayName = itemView.findViewById(R.id.displayNameTextView);
- accept = itemView.findViewById(R.id.acceptButton);
- reject = itemView.findViewById(R.id.rejectButton);
- animateAvatar = PreferenceManager.getDefaultSharedPreferences(itemView.getContext())
- .getBoolean("animateGifAvatars", false);
- }
-
- void setupWithAccount(Account account) {
- id = account.getId();
- CharSequence emojifiedName = CustomEmojiHelper.emojifyString(account.getName(), account.getEmojis(), displayName);
- displayName.setText(emojifiedName);
- String format = username.getContext().getString(R.string.status_username_format);
- String formattedUsername = String.format(format, account.getUsername());
- username.setText(formattedUsername);
- int avatarRadius = avatar.getContext().getResources()
- .getDimensionPixelSize(R.dimen.avatar_radius_48dp);
- ImageLoadingHelper.loadAvatar(account.getAvatar(), avatar, avatarRadius, animateAvatar);
- }
-
- void setupActionListener(final AccountActionListener listener) {
- accept.setOnClickListener(v -> {
- int position = getAdapterPosition();
- if (position != RecyclerView.NO_POSITION) {
- listener.onRespondToFollowRequest(true, id, position);
- }
- });
- reject.setOnClickListener(v -> {
- int position = getAdapterPosition();
- if (position != RecyclerView.NO_POSITION) {
- listener.onRespondToFollowRequest(false, id, position);
- }
- });
- avatar.setOnClickListener(v -> listener.onViewAccount(id));
- }
- }
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
index b12bab338..ba8870b0b 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
@@ -42,6 +42,7 @@ import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.entity.Account;
import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.Notification;
+import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.interfaces.LinkListener;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CardViewMode;
@@ -72,8 +73,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
private static final int VIEW_TYPE_STATUS = 0;
private static final int VIEW_TYPE_STATUS_NOTIFICATION = 1;
private static final int VIEW_TYPE_FOLLOW = 2;
- private static final int VIEW_TYPE_PLACEHOLDER = 3;
- private static final int VIEW_TYPE_UNKNOWN = 4;
+ private static final int VIEW_TYPE_FOLLOW_REQUEST = 3;
+ private static final int VIEW_TYPE_PLACEHOLDER = 4;
+ private static final int VIEW_TYPE_UNKNOWN = 5;
private static final InputFilter[] COLLAPSE_INPUT_FILTER = new InputFilter[]{SmartLengthInputFilter.INSTANCE};
private static final InputFilter[] NO_INPUT_FILTER = new InputFilter[0];
@@ -82,6 +84,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
private StatusDisplayOptions statusDisplayOptions;
private StatusActionListener statusListener;
private NotificationActionListener notificationActionListener;
+ private AccountActionListener accountActionListener;
private BidiFormatter bidiFormatter;
private AdapterDataSource dataSource;
@@ -89,13 +92,15 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
AdapterDataSource dataSource,
StatusDisplayOptions statusDisplayOptions,
StatusActionListener statusListener,
- NotificationActionListener notificationActionListener) {
+ NotificationActionListener notificationActionListener,
+ AccountActionListener accountActionListener) {
this.accountId = accountId;
this.dataSource = dataSource;
this.statusDisplayOptions = statusDisplayOptions;
this.statusListener = statusListener;
this.notificationActionListener = notificationActionListener;
+ this.accountActionListener = accountActionListener;
bidiFormatter = BidiFormatter.getInstance();
}
@@ -119,6 +124,11 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
.inflate(R.layout.item_follow, parent, false);
return new FollowViewHolder(view, statusDisplayOptions);
}
+ case VIEW_TYPE_FOLLOW_REQUEST: {
+ View view = inflater
+ .inflate(R.layout.item_follow_request_notification, parent, false);
+ return new FollowRequestViewHolder(view, true);
+ }
case VIEW_TYPE_PLACEHOLDER: {
View view = inflater
.inflate(R.layout.item_status_placeholder, parent, false);
@@ -215,6 +225,13 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
}
break;
}
+ case VIEW_TYPE_FOLLOW_REQUEST: {
+ if (payloadForHolder == null) {
+ FollowRequestViewHolder holder = (FollowRequestViewHolder) viewHolder;
+ holder.setupWithAccount(concreteNotificaton.getAccount(), bidiFormatter);
+ holder.setupActionListener(accountActionListener);
+ }
+ }
default:
}
}
@@ -258,6 +275,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
case FOLLOW: {
return VIEW_TYPE_FOLLOW;
}
+ case FOLLOW_REQUEST: {
+ return VIEW_TYPE_FOLLOW_REQUEST;
+ }
default: {
return VIEW_TYPE_UNKNOWN;
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt
index e4948c3d0..77ce16ccb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt
@@ -39,6 +39,7 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
var notificationsEnabled: Boolean = true,
var notificationsMentioned: Boolean = true,
var notificationsFollowed: Boolean = true,
+ var notificationsFollowRequested: Boolean = false,
var notificationsReblogged: Boolean = true,
var notificationsFavorited: Boolean = true,
var notificationsPolls: Boolean = true,
@@ -54,7 +55,7 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
var activeNotifications: String = "[]",
var emojis: List = emptyList(),
var tabPreferences: List = defaultTabs(),
- var notificationsFilter: String = "[]") {
+ var notificationsFilter: String = "[\"follow_request\"]") {
val identifier: String
get() = "$domain:$accountId"
diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java
index e4d743c98..dc32eea7c 100644
--- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java
+++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java
@@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
TimelineAccountEntity.class, ConversationEntity.class
- }, version = 21)
+ }, version = 22)
public abstract class AppDatabase extends RoomDatabase {
public abstract TootDao tootDao();
@@ -326,4 +326,11 @@ public abstract class AppDatabase extends RoomDatabase {
}
};
-}
\ No newline at end of file
+ public static final Migration MIGRATION_21_22 = new Migration(21, 22) {
+ @Override
+ public void migrate(@NonNull SupportSQLiteDatabase database) {
+ database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `notificationsFollowRequested` INTEGER NOT NULL DEFAULT 0");
+ }
+ };
+
+}
diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
index 69194c795..669bd216a 100644
--- a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
@@ -79,7 +79,7 @@ class AppModule {
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
- AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21)
+ AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22)
.build()
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt
index d9459ea80..fe342e7ae 100644
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Notification.kt
@@ -31,6 +31,7 @@ data class Notification(
REBLOG("reblog"),
FAVOURITE("favourite"),
FOLLOW("follow"),
+ FOLLOW_REQUEST("follow_request"),
POLL("poll");
companion object {
@@ -43,7 +44,7 @@ data class Notification(
}
return UNKNOWN
}
- val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, POLL)
+ val asList = listOf(MENTION, REBLOG, FAVOURITE, FOLLOW, FOLLOW_REQUEST, POLL)
}
override fun toString(): String {
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
index e8a4b5f6c..41720f273 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java
@@ -65,7 +65,9 @@ import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.di.Injectable;
import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.entity.Poll;
+import com.keylesspalace.tusky.entity.Relationship;
import com.keylesspalace.tusky.entity.Status;
+import com.keylesspalace.tusky.interfaces.AccountActionListener;
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
@@ -98,6 +100,7 @@ import javax.inject.Inject;
import at.connyduck.sparkbutton.helpers.Utils;
import io.reactivex.Observable;
+import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
@@ -115,6 +118,7 @@ public class NotificationsFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener,
StatusActionListener,
NotificationsAdapter.NotificationActionListener,
+ AccountActionListener,
Injectable, ReselectableFragment {
private static final String TAG = "NotificationF"; // logging tag
@@ -251,7 +255,7 @@ public class NotificationsFragment extends SFragment implements
);
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
- dataSource, statusDisplayOptions, this, this);
+ dataSource, statusDisplayOptions, this, this, this);
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
alwaysOpenSpoiler = accountManager.getActiveAccount().getAlwaysOpenSpoiler();
recyclerView.setAdapter(adapter);
@@ -765,6 +769,8 @@ public class NotificationsFragment extends SFragment implements
return getString(R.string.notification_boost_name);
case FOLLOW:
return getString(R.string.notification_follow_name);
+ case FOLLOW_REQUEST:
+ return getString(R.string.notification_follow_request_name);
case POLL:
return getString(R.string.notification_poll_name);
default:
@@ -817,6 +823,29 @@ public class NotificationsFragment extends SFragment implements
super.viewAccount(id);
}
+ @Override
+ public void onMute(boolean mute, String id, int position) {
+ // No muting from notifications yet
+ }
+
+ @Override
+ public void onBlock(boolean block, String id, int position) {
+ // No blocking from notifications yet
+ }
+
+ @Override
+ public void onRespondToFollowRequest(boolean accept, String id, int position) {
+ Single request = accept ?
+ mastodonApi.authorizeFollowRequestObservable(id) :
+ mastodonApi.rejectFollowRequestObservable(id);
+ request.observeOn(AndroidSchedulers.mainThread())
+ .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
+ .subscribe(
+ (relationship) -> fullyRefreshWithProgressBar(true),
+ (error) -> Log.e(TAG, String.format("Failed to %s account id %s", accept ? "accept" : "reject", id))
+ );
+ }
+
@Override
public void onViewStatusForNotificationId(String notificationId) {
for (Either either : notifications) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt
index 9103775ea..093c23082 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt
@@ -41,42 +41,23 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.O
val activeAccount = accountManager.activeAccount
if (activeAccount != null) {
-
- val notificationPref = requirePreference("notificationsEnabled") as SwitchPreferenceCompat
- notificationPref.isChecked = activeAccount.notificationsEnabled
- notificationPref.onPreferenceChangeListener = this
-
- val mentionedPref = requirePreference("notificationFilterMentions") as SwitchPreferenceCompat
- mentionedPref.isChecked = activeAccount.notificationsMentioned
- mentionedPref.onPreferenceChangeListener = this
-
- val followedPref = requirePreference("notificationFilterFollows") as SwitchPreferenceCompat
- followedPref.isChecked = activeAccount.notificationsFollowed
- followedPref.onPreferenceChangeListener = this
-
- val boostedPref = requirePreference("notificationFilterReblogs") as SwitchPreferenceCompat
- boostedPref.isChecked = activeAccount.notificationsReblogged
- boostedPref.onPreferenceChangeListener = this
-
- val favoritedPref = requirePreference("notificationFilterFavourites") as SwitchPreferenceCompat
- favoritedPref.isChecked = activeAccount.notificationsFavorited
- favoritedPref.onPreferenceChangeListener = this
-
- val pollsPref = requirePreference("notificationFilterPolls") as SwitchPreferenceCompat
- pollsPref.isChecked = activeAccount.notificationsPolls
- pollsPref.onPreferenceChangeListener = this
-
- val soundPref = requirePreference("notificationAlertSound") as SwitchPreferenceCompat
- soundPref.isChecked = activeAccount.notificationSound
- soundPref.onPreferenceChangeListener = this
-
- val vibrationPref = requirePreference("notificationAlertVibrate") as SwitchPreferenceCompat
- vibrationPref.isChecked = activeAccount.notificationVibration
- vibrationPref.onPreferenceChangeListener = this
-
- val lightPref = requirePreference("notificationAlertLight") as SwitchPreferenceCompat
- lightPref.isChecked = activeAccount.notificationLight
- lightPref.onPreferenceChangeListener = this
+ for (pair in mapOf(
+ "notificationsEnabled" to activeAccount.notificationsEnabled,
+ "notificationFilterMentions" to activeAccount.notificationsMentioned,
+ "notificationFilterFollows" to activeAccount.notificationsFollowed,
+ "notificationFilterFollowRequests" to activeAccount.notificationsFollowRequested,
+ "notificationFilterReblogs" to activeAccount.notificationsReblogged,
+ "notificationFilterFavourites" to activeAccount.notificationsFavorited,
+ "notificationFilterPolls" to activeAccount.notificationsPolls,
+ "notificationAlertSound" to activeAccount.notificationSound,
+ "notificationAlertVibrate" to activeAccount.notificationVibration,
+ "notificationAlertLight" to activeAccount.notificationLight
+ )) {
+ (requirePreference(pair.key) as SwitchPreferenceCompat).apply {
+ isChecked = pair.value
+ onPreferenceChangeListener = this@NotificationPreferencesFragment
+ }
+ }
}
}
@@ -96,6 +77,7 @@ class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.O
}
"notificationFilterMentions" -> activeAccount.notificationsMentioned = newValue as Boolean
"notificationFilterFollows" -> activeAccount.notificationsFollowed = newValue as Boolean
+ "notificationFilterFollowRequests" -> activeAccount.notificationsFollowRequested = newValue as Boolean
"notificationFilterReblogs" -> activeAccount.notificationsReblogged = newValue as Boolean
"notificationFilterFavourites" -> activeAccount.notificationsFavorited = newValue as Boolean
"notificationFilterPolls" -> activeAccount.notificationsPolls = newValue as Boolean
diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
index ccd8371b7..7c4b454a3 100644
--- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
@@ -383,6 +383,16 @@ interface MastodonApi {
@Path("id") accountId: String
): Call
+ @POST("api/v1/follow_requests/{id}/authorize")
+ fun authorizeFollowRequestObservable(
+ @Path("id") accountId: String
+ ): Single
+
+ @POST("api/v1/follow_requests/{id}/reject")
+ fun rejectFollowRequestObservable(
+ @Path("id") accountId: String
+ ): Single
+
@FormUrlEncoded
@POST("api/v1/apps")
fun authenticateApp(
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
index 4e0d298d7..8b526dc57 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
@@ -113,6 +113,7 @@ public class NotificationHelper {
**/
public static final String CHANNEL_MENTION = "CHANNEL_MENTION";
public static final String CHANNEL_FOLLOW = "CHANNEL_FOLLOW";
+ public static final String CHANNEL_FOLLOW_REQUEST = "CHANNEL_FOLLOW_REQUEST";
public static final String CHANNEL_BOOST = "CHANNEL_BOOST";
public static final String CHANNEL_FAVOURITE = "CHANNEL_FAVOURITE";
public static final String CHANNEL_POLL = "CHANNEL_POLL";
@@ -348,6 +349,7 @@ public class NotificationHelper {
String[] channelIds = new String[]{
CHANNEL_MENTION + account.getIdentifier(),
CHANNEL_FOLLOW + account.getIdentifier(),
+ CHANNEL_FOLLOW_REQUEST + account.getIdentifier(),
CHANNEL_BOOST + account.getIdentifier(),
CHANNEL_FAVOURITE + account.getIdentifier(),
CHANNEL_POLL + account.getIdentifier(),
@@ -355,6 +357,7 @@ public class NotificationHelper {
int[] channelNames = {
R.string.notification_mention_name,
R.string.notification_follow_name,
+ R.string.notification_follow_request_name,
R.string.notification_boost_name,
R.string.notification_favourite_name,
R.string.notification_poll_name
@@ -362,12 +365,13 @@ public class NotificationHelper {
int[] channelDescriptions = {
R.string.notification_mention_descriptions,
R.string.notification_follow_description,
+ R.string.notification_follow_request_description,
R.string.notification_boost_description,
R.string.notification_favourite_description,
R.string.notification_poll_description
};
- List channels = new ArrayList<>(5);
+ List channels = new ArrayList<>(6);
NotificationChannelGroup channelGroup = new NotificationChannelGroup(account.getIdentifier(), account.getFullName());
@@ -508,6 +512,8 @@ public class NotificationHelper {
return account.getNotificationsMentioned();
case FOLLOW:
return account.getNotificationsFollowed();
+ case FOLLOW_REQUEST:
+ return account.getNotificationsFollowRequested();
case REBLOG:
return account.getNotificationsReblogged();
case FAVOURITE:
@@ -525,6 +531,8 @@ public class NotificationHelper {
return CHANNEL_MENTION + account.getIdentifier();
case FOLLOW:
return CHANNEL_FOLLOW + account.getIdentifier();
+ case FOLLOW_REQUEST:
+ return CHANNEL_FOLLOW_REQUEST + account.getIdentifier();
case REBLOG:
return CHANNEL_BOOST + account.getIdentifier();
case FAVOURITE:
@@ -594,6 +602,9 @@ public class NotificationHelper {
case FOLLOW:
return String.format(context.getString(R.string.notification_follow_format),
accountName);
+ case FOLLOW_REQUEST:
+ return String.format(context.getString(R.string.notification_follow_request_format),
+ accountName);
case FAVOURITE:
return String.format(context.getString(R.string.notification_favourite_format),
accountName);
@@ -613,6 +624,7 @@ public class NotificationHelper {
private static String bodyForType(Notification notification, Context context) {
switch (notification.getType()) {
case FOLLOW:
+ case FOLLOW_REQUEST:
return "@" + notification.getAccount().getUsername();
case MENTION:
case FAVOURITE:
diff --git a/app/src/main/res/layout/item_follow_request_notification.xml b/app/src/main/res/layout/item_follow_request_notification.xml
new file mode 100644
index 000000000..712c08d42
--- /dev/null
+++ b/app/src/main/res/layout/item_follow_request_notification.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c21bdc9df..7a91facb7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -60,6 +60,7 @@
%s boosted your toot
%s favorited your toot
%s followed you
+ %s requested to follow you
Report @%s
Additional comments?
@@ -208,6 +209,7 @@
Notify me when
mentioned
followed
+ follow requested
my posts are boosted
my posts are favorited
polls have ended
@@ -262,6 +264,8 @@
Notifications about new mentions
New Followers
Notifications about new followers
+ Follow Requests
+ Notifications about follow requests
Boosts
Notifications when your toots get boosted
Favorites
diff --git a/app/src/main/res/xml/notification_preferences.xml b/app/src/main/res/xml/notification_preferences.xml
index 8bd044147..fa03f8d1b 100644
--- a/app/src/main/res/xml/notification_preferences.xml
+++ b/app/src/main/res/xml/notification_preferences.xml
@@ -27,6 +27,12 @@
android:title="@string/pref_title_notification_filter_follows"
app:iconSpaceReserved="false" />
+
+
Date: Sun, 8 Mar 2020 09:42:03 +0000
Subject: [PATCH 28/42] Translated using Weblate (Swedish)
Currently translated at 75.0% (6 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/sv/
---
fastlane/metadata/android/sv/changelogs/70.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 fastlane/metadata/android/sv/changelogs/70.txt
diff --git a/fastlane/metadata/android/sv/changelogs/70.txt b/fastlane/metadata/android/sv/changelogs/70.txt
new file mode 100644
index 000000000..eb2428746
--- /dev/null
+++ b/fastlane/metadata/android/sv/changelogs/70.txt
@@ -0,0 +1,8 @@
+Tusky v10.0
+
+- You can now bookmark statuses & list your bookmarks in Tusky.
+- You can now schedule toots with Tusky. Note that the time you select has to be at least 5 minutes in the future.
+- You can now add lists to the main screen.
+- You can now post audio attachments with Tusky.
+
+And a lot of other small improvements and bug fixes!
From 73c47301105cbab1a0ef9c9f65ef06d92b09fa2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Quent=C3=AD?=
Date: Sun, 22 Mar 2020 23:42:31 +0000
Subject: [PATCH 29/42] Translated using Weblate (Occitan)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/oc/
---
app/src/main/res/values-oc/strings.xml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml
index 31e2a0406..ea8266f6c 100644
--- a/app/src/main/res/values-oc/strings.xml
+++ b/app/src/main/res/values-oc/strings.xml
@@ -491,4 +491,5 @@
Avètz pas cap de borrolhon.
Avètz pas cap de tut planificat.
-
+L’interval minimum de planificacion sus Mastodon e de 5 minutas.
+
From 7973bf7d180636d4008162dc87327e8614880ff1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Francesc=20Gal=C3=AD?=
Date: Sun, 22 Mar 2020 23:42:31 +0000
Subject: [PATCH 30/42] Translated using Weblate (Catalan)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/ca/
---
app/src/main/res/values-ca/strings.xml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index ce4efb9c8..e62c1b076 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -534,4 +534,7 @@
No tens cap estat planificat.
-
+Els fitxers d\'àudio han de ser més petits de 40MB.
+ No tens cap esborrany.
+ L\'interval mínim de planificació a Mastodon és de 5 minuts.
+
From a25360645887e4ef1dc06735d481b76a798c985a Mon Sep 17 00:00:00 2001
From: Juanjo Salvador
Date: Sun, 22 Mar 2020 23:42:31 +0000
Subject: [PATCH 31/42] Translated using Weblate (Spanish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/es/
---
app/src/main/res/values-es/strings.xml | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0ae66004d..f0629173d 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -474,9 +474,10 @@
Seleccionar lista
Lista
Los ficheros de audio deben ser menores de 40MB.
- Mostrar degradados coloridos para el contenido multimedia oculto.
+ Mostrar degradados coloridos para el contenido multimedia oculto
No tienes ningún borrador.
No tienes ningún estado programado.
-
+Mastodon tiene un intervalo de programación mínimo de 5 minutos.
+
From 8cb83050acdaa98582e0a617309a73d377061eca Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Tue, 24 Mar 2020 21:06:04 +0100
Subject: [PATCH 32/42] Add support for muting conversations (#1732)
* Add support for muting conversations
Implements #1731
* Fix CI
* Apply code review feedback
---
.../23.json | 741 ++++++++++++++++++
.../keylesspalace/tusky/appstore/Events.kt | 1 +
.../conversation/ConversationEntity.kt | 1 +
.../components/search/SearchViewModel.kt | 12 +
.../fragments/SearchStatusesFragment.kt | 37 +-
.../keylesspalace/tusky/db/AppDatabase.java | 9 +-
.../tusky/db/TimelineStatusEntity.kt | 3 +-
.../com/keylesspalace/tusky/di/AppModule.kt | 5 +-
.../com/keylesspalace/tusky/entity/Status.kt | 1 +
.../tusky/fragment/SFragment.java | 43 +-
.../tusky/fragment/TimelineFragment.java | 8 +
.../tusky/network/MastodonApi.kt | 10 +
.../tusky/network/TimelineCases.kt | 15 +-
.../tusky/repository/TimelineRepository.kt | 9 +-
.../tusky/viewdata/StatusViewData.java | 17 +-
app/src/main/res/menu/status_more.xml | 3 +
.../main/res/menu/status_more_for_user.xml | 3 +
app/src/main/res/values/strings.xml | 2 +
.../tusky/BottomSheetActivityTest.kt | 1 +
.../com/keylesspalace/tusky/FilterTest.kt | 1 +
.../tusky/fragment/TimelineRepositoryTest.kt | 1 +
21 files changed, 904 insertions(+), 19 deletions(-)
create mode 100644 app/schemas/com.keylesspalace.tusky.db.AppDatabase/23.json
diff --git a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/23.json b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/23.json
new file mode 100644
index 000000000..d7f2b2971
--- /dev/null
+++ b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/23.json
@@ -0,0 +1,741 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 23,
+ "identityHash": "03a7436643ef356198742c5f8e054f5f",
+ "entities": [
+ {
+ "tableName": "TootEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `text` TEXT, `urls` TEXT, `descriptions` TEXT, `contentWarning` TEXT, `inReplyToId` TEXT, `inReplyToText` TEXT, `inReplyToUsername` TEXT, `visibility` INTEGER, `poll` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "uid",
+ "columnName": "uid",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "text",
+ "columnName": "text",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "urls",
+ "columnName": "urls",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "descriptions",
+ "columnName": "descriptions",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "contentWarning",
+ "columnName": "contentWarning",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToId",
+ "columnName": "inReplyToId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToText",
+ "columnName": "inReplyToText",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToUsername",
+ "columnName": "inReplyToUsername",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "visibility",
+ "columnName": "visibility",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "poll",
+ "columnName": "poll",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "uid"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AccountEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `domain` TEXT NOT NULL, `accessToken` TEXT NOT NULL, `isActive` INTEGER NOT NULL, `accountId` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `profilePictureUrl` TEXT NOT NULL, `notificationsEnabled` INTEGER NOT NULL, `notificationsMentioned` INTEGER NOT NULL, `notificationsFollowed` INTEGER NOT NULL, `notificationsFollowRequested` INTEGER NOT NULL, `notificationsReblogged` INTEGER NOT NULL, `notificationsFavorited` INTEGER NOT NULL, `notificationsPolls` INTEGER NOT NULL, `notificationSound` INTEGER NOT NULL, `notificationVibration` INTEGER NOT NULL, `notificationLight` INTEGER NOT NULL, `defaultPostPrivacy` INTEGER NOT NULL, `defaultMediaSensitivity` INTEGER NOT NULL, `alwaysShowSensitiveMedia` INTEGER NOT NULL, `alwaysOpenSpoiler` INTEGER NOT NULL, `mediaPreviewEnabled` INTEGER NOT NULL, `lastNotificationId` TEXT NOT NULL, `activeNotifications` TEXT NOT NULL, `emojis` TEXT NOT NULL, `tabPreferences` TEXT NOT NULL, `notificationsFilter` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "domain",
+ "columnName": "domain",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accessToken",
+ "columnName": "accessToken",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isActive",
+ "columnName": "isActive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "displayName",
+ "columnName": "displayName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "profilePictureUrl",
+ "columnName": "profilePictureUrl",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsEnabled",
+ "columnName": "notificationsEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsMentioned",
+ "columnName": "notificationsMentioned",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFollowed",
+ "columnName": "notificationsFollowed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFollowRequested",
+ "columnName": "notificationsFollowRequested",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsReblogged",
+ "columnName": "notificationsReblogged",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFavorited",
+ "columnName": "notificationsFavorited",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsPolls",
+ "columnName": "notificationsPolls",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationSound",
+ "columnName": "notificationSound",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationVibration",
+ "columnName": "notificationVibration",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationLight",
+ "columnName": "notificationLight",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "defaultPostPrivacy",
+ "columnName": "defaultPostPrivacy",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "defaultMediaSensitivity",
+ "columnName": "defaultMediaSensitivity",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "alwaysShowSensitiveMedia",
+ "columnName": "alwaysShowSensitiveMedia",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "alwaysOpenSpoiler",
+ "columnName": "alwaysOpenSpoiler",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mediaPreviewEnabled",
+ "columnName": "mediaPreviewEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastNotificationId",
+ "columnName": "lastNotificationId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "activeNotifications",
+ "columnName": "activeNotifications",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojis",
+ "columnName": "emojis",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tabPreferences",
+ "columnName": "tabPreferences",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "notificationsFilter",
+ "columnName": "notificationsFilter",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_AccountEntity_domain_accountId",
+ "unique": true,
+ "columnNames": [
+ "domain",
+ "accountId"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_AccountEntity_domain_accountId` ON `${TABLE_NAME}` (`domain`, `accountId`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "InstanceEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`instance` TEXT NOT NULL, `emojiList` TEXT, `maximumTootCharacters` INTEGER, `maxPollOptions` INTEGER, `maxPollOptionLength` INTEGER, `version` TEXT, PRIMARY KEY(`instance`))",
+ "fields": [
+ {
+ "fieldPath": "instance",
+ "columnName": "instance",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojiList",
+ "columnName": "emojiList",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "maximumTootCharacters",
+ "columnName": "maximumTootCharacters",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "maxPollOptions",
+ "columnName": "maxPollOptions",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "maxPollOptionLength",
+ "columnName": "maxPollOptionLength",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "version",
+ "columnName": "version",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "instance"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimelineStatusEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT, `visibility` INTEGER, `attachments` TEXT, `mentions` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "serverId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "timelineUserId",
+ "columnName": "timelineUserId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "authorServerId",
+ "columnName": "authorServerId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToId",
+ "columnName": "inReplyToId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "inReplyToAccountId",
+ "columnName": "inReplyToAccountId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "createdAt",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojis",
+ "columnName": "emojis",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "reblogsCount",
+ "columnName": "reblogsCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "favouritesCount",
+ "columnName": "favouritesCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "reblogged",
+ "columnName": "reblogged",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "bookmarked",
+ "columnName": "bookmarked",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "favourited",
+ "columnName": "favourited",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sensitive",
+ "columnName": "sensitive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "spoilerText",
+ "columnName": "spoilerText",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "visibility",
+ "columnName": "visibility",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "mentions",
+ "columnName": "mentions",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "application",
+ "columnName": "application",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "reblogServerId",
+ "columnName": "reblogServerId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "reblogAccountId",
+ "columnName": "reblogAccountId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "poll",
+ "columnName": "poll",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "muted",
+ "columnName": "muted",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "serverId",
+ "timelineUserId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [
+ {
+ "name": "index_TimelineStatusEntity_authorServerId_timelineUserId",
+ "unique": false,
+ "columnNames": [
+ "authorServerId",
+ "timelineUserId"
+ ],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_TimelineStatusEntity_authorServerId_timelineUserId` ON `${TABLE_NAME}` (`authorServerId`, `timelineUserId`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "TimelineAccountEntity",
+ "onDelete": "NO ACTION",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "authorServerId",
+ "timelineUserId"
+ ],
+ "referencedColumns": [
+ "serverId",
+ "timelineUserId"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "TimelineAccountEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `timelineUserId` INTEGER NOT NULL, `localUsername` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `url` TEXT NOT NULL, `avatar` TEXT NOT NULL, `emojis` TEXT NOT NULL, `bot` INTEGER NOT NULL, PRIMARY KEY(`serverId`, `timelineUserId`))",
+ "fields": [
+ {
+ "fieldPath": "serverId",
+ "columnName": "serverId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timelineUserId",
+ "columnName": "timelineUserId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "localUsername",
+ "columnName": "localUsername",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "displayName",
+ "columnName": "displayName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatar",
+ "columnName": "avatar",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "emojis",
+ "columnName": "emojis",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "bot",
+ "columnName": "bot",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "serverId",
+ "timelineUserId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ConversationEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `id` TEXT NOT NULL, `accounts` TEXT NOT NULL, `unread` INTEGER NOT NULL, `s_id` TEXT NOT NULL, `s_url` TEXT, `s_inReplyToId` TEXT, `s_inReplyToAccountId` TEXT, `s_account` TEXT NOT NULL, `s_content` TEXT NOT NULL, `s_createdAt` INTEGER NOT NULL, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_favourited` INTEGER NOT NULL, `s_bookmarked` INTEGER NOT NULL, `s_sensitive` INTEGER NOT NULL, `s_spoilerText` TEXT NOT NULL, `s_attachments` TEXT NOT NULL, `s_mentions` TEXT NOT NULL, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsible` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_poll` TEXT, PRIMARY KEY(`id`, `accountId`))",
+ "fields": [
+ {
+ "fieldPath": "accountId",
+ "columnName": "accountId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "accounts",
+ "columnName": "accounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.id",
+ "columnName": "s_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.url",
+ "columnName": "s_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastStatus.inReplyToId",
+ "columnName": "s_inReplyToId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastStatus.inReplyToAccountId",
+ "columnName": "s_inReplyToAccountId",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "lastStatus.account",
+ "columnName": "s_account",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.content",
+ "columnName": "s_content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.createdAt",
+ "columnName": "s_createdAt",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.emojis",
+ "columnName": "s_emojis",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.favouritesCount",
+ "columnName": "s_favouritesCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.favourited",
+ "columnName": "s_favourited",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.bookmarked",
+ "columnName": "s_bookmarked",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.sensitive",
+ "columnName": "s_sensitive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.spoilerText",
+ "columnName": "s_spoilerText",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.attachments",
+ "columnName": "s_attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.mentions",
+ "columnName": "s_mentions",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.showingHiddenContent",
+ "columnName": "s_showingHiddenContent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.expanded",
+ "columnName": "s_expanded",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.collapsible",
+ "columnName": "s_collapsible",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.collapsed",
+ "columnName": "s_collapsed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastStatus.poll",
+ "columnName": "s_poll",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id",
+ "accountId"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '03a7436643ef356198742c5f8e054f5f')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt
index 7bdc17e62..8e62faeb3 100644
--- a/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt
@@ -8,6 +8,7 @@ import com.keylesspalace.tusky.entity.Status
data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatchable
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
+data class MuteConversationEvent(val statusId: String, val mute: Boolean) : Dispatchable
data class UnfollowEvent(val accountId: String) : Dispatchable
data class BlockEvent(val accountId: String) : Dispatchable
data class MuteEvent(val accountId: String) : Dispatchable
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt
index 7c4ed108b..8ee1a284a 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt
@@ -157,6 +157,7 @@ data class ConversationStatusEntity(
mentions = mentions,
application = null,
pinned = false,
+ muted = false,
poll = poll,
card = null)
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt
index 7979fee55..71121a91d 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt
@@ -213,6 +213,18 @@ class SearchViewModel @Inject constructor(
search(currentQuery)
}
+ fun muteConversation(status: Pair, mute: Boolean) {
+ val idx = loadedStatuses.indexOf(status)
+ if (idx >= 0) {
+ val newPair = Pair(status.first, StatusViewData.Builder(status.second).setMuted(mute).createStatusViewData())
+ loadedStatuses[idx] = newPair
+ repoResultStatus.value?.refresh?.invoke()
+ }
+ timelineCases.muteConversation(status.first, mute)
+ .onErrorReturnItem(status.first)
+ .subscribe()
+ .autoDispose()
+ }
companion object {
private const val TAG = "SearchViewModel"
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
index ac171b9e7..0833f04cd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
@@ -49,6 +49,7 @@ import com.keylesspalace.tusky.components.search.adapter.SearchStatusesAdapter
import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.entity.Status
+import com.keylesspalace.tusky.entity.Status.Mention
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.util.CardViewMode
@@ -228,12 +229,9 @@ class SearchStatusesFragment : SearchFragment {
} //Ignore
}
+ } else {
+ popup.inflate(R.menu.status_more)
+ val menu = popup.menu
+ menu.findItem(R.id.status_download_media).isVisible = status.attachments.isNotEmpty()
}
val openAsItem = popup.menu.findItem(R.id.status_open_as)
@@ -266,6 +268,19 @@ class SearchStatusesFragment : SearchFragment
when (item.itemId) {
R.id.status_share_content -> {
@@ -303,6 +318,12 @@ class SearchStatusesFragment : SearchFragment {
+ searchAdapter.getItem(position)?.let { foundStatus ->
+ viewModel.muteConversation(foundStatus, status.muted != true)
+ }
+ return@setOnMenuItemClickListener true
+ }
R.id.status_mute -> {
viewModel.muteAcount(accountId)
return@setOnMenuItemClickListener true
@@ -341,6 +362,12 @@ class SearchStatusesFragment : SearchFragment): Boolean {
+ return mentions.firstOrNull {
+ account?.username == it.username && account.domain == Uri.parse(it.url)?.host
+ } != null
+ }
+
private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence) {
bottomSheetActivity?.showAccountChooserDialog(dialogTitle, false, object : AccountSelectionListener {
override fun onAccountSelected(account: AccountEntity) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java
index dc32eea7c..608333d93 100644
--- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java
+++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java
@@ -30,7 +30,7 @@ import androidx.annotation.NonNull;
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class, TimelineStatusEntity.class,
TimelineAccountEntity.class, ConversationEntity.class
- }, version = 22)
+ }, version = 23)
public abstract class AppDatabase extends RoomDatabase {
public abstract TootDao tootDao();
@@ -333,4 +333,11 @@ public abstract class AppDatabase extends RoomDatabase {
}
};
+ public static final Migration MIGRATION_22_23 = new Migration(22, 23) {
+ @Override
+ public void migrate(@NonNull SupportSQLiteDatabase database) {
+ database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `muted` INTEGER");
+ }
+ };
+
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt
index 94111a954..da98cb4b3 100644
--- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt
@@ -51,7 +51,8 @@ data class TimelineStatusEntity(
val application: String?,
val reblogServerId: String?, // if it has a reblogged status, it's id is stored here
val reblogAccountId: String?,
- val poll: String?
+ val poll: String?,
+ val muted: Boolean?
)
@Entity(
diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
index 669bd216a..a4f2eb5eb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
@@ -79,8 +79,9 @@ class AppModule {
AppDatabase.MIGRATION_11_12, AppDatabase.MIGRATION_12_13, AppDatabase.MIGRATION_10_13,
AppDatabase.MIGRATION_13_14, AppDatabase.MIGRATION_14_15, AppDatabase.MIGRATION_15_16,
AppDatabase.MIGRATION_16_17, AppDatabase.MIGRATION_17_18, AppDatabase.MIGRATION_18_19,
- AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22)
- .build()
+ AppDatabase.MIGRATION_19_20, AppDatabase.MIGRATION_20_21, AppDatabase.MIGRATION_21_22,
+ AppDatabase.MIGRATION_22_23)
+ .build()
}
@Provides
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt
index 2ccf0dcb2..aee30e8d7 100644
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Status.kt
@@ -43,6 +43,7 @@ data class Status(
val mentions: Array,
val application: Application?,
var pinned: Boolean?,
+ var muted: Boolean?,
val poll: Poll?,
val card: Card?
) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
index 4f795f183..6a7e5f12d 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
@@ -185,11 +185,8 @@ public abstract class SFragment extends BaseFragment implements Injectable {
PopupMenu popup = new PopupMenu(getContext(), view);
// Give a different menu depending on whether this is the user's own toot or not.
- if (loggedInAccountId == null || !loggedInAccountId.equals(accountId)) {
- popup.inflate(R.menu.status_more);
- Menu menu = popup.getMenu();
- menu.findItem(R.id.status_download_media).setVisible(!status.getAttachments().isEmpty());
- } else {
+ boolean statusIsByCurrentUser = loggedInAccountId != null && loggedInAccountId.equals(accountId);
+ if (statusIsByCurrentUser) {
popup.inflate(R.menu.status_more_for_user);
Menu menu = popup.getMenu();
switch (status.getVisibility()) {
@@ -208,6 +205,10 @@ public abstract class SFragment extends BaseFragment implements Injectable {
break;
}
}
+ } else {
+ popup.inflate(R.menu.status_more);
+ Menu menu = popup.getMenu();
+ menu.findItem(R.id.status_download_media).setVisible(!status.getAttachments().isEmpty());
}
Menu menu = popup.getMenu();
@@ -231,6 +232,15 @@ public abstract class SFragment extends BaseFragment implements Injectable {
}
openAsItem.setTitle(openAsTitle);
+ MenuItem muteConversationItem = menu.findItem(R.id.status_mute_conversation);
+ boolean mutable = statusIsByCurrentUser || accountIsInMentions(activeAccount, status.getMentions());
+ muteConversationItem.setVisible(mutable);
+ if (mutable) {
+ muteConversationItem.setTitle((status.getMuted() == null || !status.getMuted()) ?
+ R.string.action_mute_conversation :
+ R.string.action_unmute_conversation);
+ }
+
popup.setOnMenuItemClickListener(item -> {
switch (item.getItemId()) {
case R.id.status_share_content: {
@@ -305,12 +315,35 @@ public abstract class SFragment extends BaseFragment implements Injectable {
timelineCases.pin(status, !status.isPinned());
return true;
}
+ case R.id.status_mute_conversation: {
+ timelineCases.muteConversation(status, status.getMuted() == null || !status.getMuted())
+ .observeOn(AndroidSchedulers.mainThread())
+ .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
+ .subscribe();
+ return true;
+ }
}
return false;
});
popup.show();
}
+ private static boolean accountIsInMentions(AccountEntity account, Status.Mention[] mentions) {
+ if (account == null) {
+ return false;
+ }
+
+ for (Status.Mention mention : mentions) {
+ if (account.getUsername().equals(mention.getUsername())) {
+ Uri uri = Uri.parse(mention.getUrl());
+ if (uri != null && account.getDomain().equals(uri.getHost())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
protected void viewMedia(int urlIndex, Status status, @Nullable View view) {
final Status actionable = status.getActionableStatus();
final Attachment active = actionable.getAttachments().get(urlIndex);
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
index 8db927b29..0c20cfafe 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
@@ -53,6 +53,7 @@ import com.keylesspalace.tusky.appstore.BookmarkEvent;
import com.keylesspalace.tusky.appstore.DomainMuteEvent;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.FavoriteEvent;
+import com.keylesspalace.tusky.appstore.MuteConversationEvent;
import com.keylesspalace.tusky.appstore.MuteEvent;
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
import com.keylesspalace.tusky.appstore.ReblogEvent;
@@ -503,6 +504,9 @@ public class TimelineFragment extends SFragment implements
} else if (event instanceof BookmarkEvent) {
BookmarkEvent bookmarkEvent = (BookmarkEvent) event;
handleBookmarkEvent(bookmarkEvent);
+ } else if (event instanceof MuteConversationEvent) {
+ MuteConversationEvent muteEvent = (MuteConversationEvent) event;
+ handleMuteConversationEvent(muteEvent);
} else if (event instanceof UnfollowEvent) {
if (kind == Kind.HOME) {
String id = ((UnfollowEvent) event).getAccountId();
@@ -1313,6 +1317,10 @@ public class TimelineFragment extends SFragment implements
setBookmarkForStatus(pos, status, bookmarkEvent.getBookmark());
}
+ private void handleMuteConversationEvent(@NonNull MuteConversationEvent event) {
+ fullyRefresh();
+ }
+
private void handleStatusComposeEvent(@NonNull Status status) {
switch (kind) {
case HOME:
diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
index 7c4b454a3..dc69ed7d5 100644
--- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt
@@ -200,6 +200,16 @@ interface MastodonApi {
@Path("id") statusId: String
): Single
+ @POST("api/v1/statuses/{id}/mute")
+ fun muteConversation(
+ @Path("id") statusId: String
+ ): Single
+
+ @POST("api/v1/statuses/{id}/unmute")
+ fun unmuteConversation(
+ @Path("id") statusId: String
+ ): Single
+
@GET("api/v1/scheduled_statuses")
fun scheduledStatuses(
@Query("limit") limit: Int? = null,
diff --git a/app/src/main/java/com/keylesspalace/tusky/network/TimelineCases.kt b/app/src/main/java/com/keylesspalace/tusky/network/TimelineCases.kt
index ce9dc7a9b..50b086982 100644
--- a/app/src/main/java/com/keylesspalace/tusky/network/TimelineCases.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/network/TimelineCases.kt
@@ -41,7 +41,7 @@ interface TimelineCases {
fun delete(id: String): Single
fun pin(status: Status, pin: Boolean)
fun voteInPoll(status: Status, choices: List): Single
-
+ fun muteConversation(status: Status, mute: Boolean): Single
}
class TimelineCasesImpl(
@@ -94,6 +94,19 @@ class TimelineCasesImpl(
}
}
+ override fun muteConversation(status: Status, mute: Boolean): Single {
+ val id = status.actionableId
+
+ val call = if (mute) {
+ mastodonApi.muteConversation(id)
+ } else {
+ mastodonApi.unmuteConversation(id)
+ }
+ return call.doAfterSuccess {
+ eventHub.dispatch(MuteConversationEvent(status.id, mute))
+ }
+ }
+
override fun mute(id: String) {
val call = mastodonApi.muteAccount(id)
call.enqueue(object : Callback {
diff --git a/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt b/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt
index 06c001c91..f16bb542c 100644
--- a/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt
@@ -229,6 +229,7 @@ class TimelineRepositoryImpl(
mentions = mentions,
application = application,
pinned = false,
+ muted = status.muted,
poll = poll,
card = null
)
@@ -256,6 +257,7 @@ class TimelineRepositoryImpl(
mentions = arrayOf(),
application = null,
pinned = false,
+ muted = status.muted,
poll = null,
card = null
)
@@ -282,6 +284,7 @@ class TimelineRepositoryImpl(
mentions = mentions,
application = application,
pinned = false,
+ muted = status.muted,
poll = poll,
card = null
)
@@ -353,7 +356,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
application = null,
reblogServerId = null,
reblogAccountId = null,
- poll = null
+ poll = null,
+ muted = false
)
}
@@ -384,7 +388,8 @@ fun Status.toEntity(timelineUserId: Long,
application = actionable.let(gson::toJson),
reblogServerId = reblog?.id,
reblogAccountId = reblog?.let { this.account.id },
- poll = actionable.poll.let(gson::toJson)
+ poll = actionable.poll.let(gson::toJson),
+ muted = actionable.muted
)
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
index b43c7628d..81e101007 100644
--- a/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
+++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/StatusViewData.java
@@ -57,6 +57,7 @@ public abstract class StatusViewData {
final boolean reblogged;
final boolean favourited;
final boolean bookmarked;
+ private final boolean muted;
@Nullable
private final String spoilerText;
private final Status.Visibility visibility;
@@ -92,7 +93,7 @@ public abstract class StatusViewData {
private final PollViewData poll;
private final boolean isBot;
- public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked,
+ public Concrete(String id, Spanned content, boolean reblogged, boolean favourited, boolean bookmarked, boolean muted,
@Nullable String spoilerText, Status.Visibility visibility, List attachments,
@Nullable String rebloggedByUsername, @Nullable String rebloggedAvatar, boolean sensitive, boolean isExpanded,
boolean isShowingContent, String userFullName, String nickname, String avatar,
@@ -115,6 +116,7 @@ public abstract class StatusViewData {
this.reblogged = reblogged;
this.favourited = favourited;
this.bookmarked = bookmarked;
+ this.muted = muted;
this.visibility = visibility;
this.attachments = attachments;
this.rebloggedByUsername = rebloggedByUsername;
@@ -161,6 +163,10 @@ public abstract class StatusViewData {
return bookmarked;
}
+ public boolean isMuted() {
+ return muted;
+ }
+
@Nullable
public String getSpoilerText() {
return spoilerText;
@@ -401,6 +407,7 @@ public abstract class StatusViewData {
private boolean reblogged;
private boolean favourited;
private boolean bookmarked;
+ private boolean muted;
private String spoilerText;
private Status.Visibility visibility;
private List attachments;
@@ -437,6 +444,7 @@ public abstract class StatusViewData {
reblogged = viewData.reblogged;
favourited = viewData.favourited;
bookmarked = viewData.bookmarked;
+ muted = viewData.muted;
spoilerText = viewData.spoilerText;
visibility = viewData.visibility;
attachments = viewData.attachments == null ? null : new ArrayList<>(viewData.attachments);
@@ -490,6 +498,11 @@ public abstract class StatusViewData {
return this;
}
+ public Builder setMuted(boolean muted) {
+ this.muted = muted;
+ return this;
+ }
+
public Builder setSpoilerText(String spoilerText) {
this.spoilerText = spoilerText;
return this;
@@ -639,7 +652,7 @@ public abstract class StatusViewData {
if (this.accountEmojis == null) accountEmojis = Collections.emptyList();
if (this.createdAt == null) createdAt = new Date();
- return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, spoilerText,
+ return new StatusViewData.Concrete(id, content, reblogged, favourited, bookmarked, muted, spoilerText,
visibility, attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded,
isShowingContent, userFullName, nickname, avatar, createdAt, reblogsCount,
favouritesCount, inReplyToId, mentions, senderId, rebloggingEnabled, application,
diff --git a/app/src/main/res/menu/status_more.xml b/app/src/main/res/menu/status_more.xml
index 525ce90a3..c73b7e9f0 100644
--- a/app/src/main/res/menu/status_more.xml
+++ b/app/src/main/res/menu/status_more.xml
@@ -21,6 +21,9 @@
+
diff --git a/app/src/main/res/menu/status_more_for_user.xml b/app/src/main/res/menu/status_more_for_user.xml
index 9b363e492..e4f391ab2 100644
--- a/app/src/main/res/menu/status_more_for_user.xml
+++ b/app/src/main/res/menu/status_more_for_user.xml
@@ -26,6 +26,9 @@
android:id="@+id/status_unreblog_private"
android:title="@string/unreblog_private"
android:visible="false" />
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7a91facb7..b517db86e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -109,6 +109,8 @@
Mute
Unmute
Mute %s
+ Mute conversation
+ Unmute conversation
Mention
Hide media
Open drawer
diff --git a/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt
index 5c92667ec..86d0925f3 100644
--- a/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/BottomSheetActivityTest.kt
@@ -88,6 +88,7 @@ class BottomSheetActivityTest {
arrayOf(),
null,
pinned = false,
+ muted = false,
poll = null,
card = null
)
diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
index 965eda674..19288499f 100644
--- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
@@ -214,6 +214,7 @@ class FilterTest {
mentions = emptyArray(),
application = null,
pinned = false,
+ muted = false,
poll = if (pollOptions != null) {
Poll(
id = "1234",
diff --git a/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt b/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt
index b55be0908..f80f57b8c 100644
--- a/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt
@@ -314,6 +314,7 @@ class TimelineRepositoryTest {
inReplyToAccountId = null,
inReplyToId = null,
pinned = false,
+ muted = false,
reblog = null,
url = "http://example.com/statuses/$id",
poll = null,
From cf782f039f5b363141877228c911f3d7132a6492 Mon Sep 17 00:00:00 2001
From: Konrad Pozniak
Date: Tue, 24 Mar 2020 21:06:58 +0100
Subject: [PATCH 33/42] use number of voters instead of votes to calculate poll
results (#1733)
* adjust poll vote text, votes -> people
* use number of voters instead of votes to calculate poll results
* fix tests
---
.../tusky/adapter/PollAdapter.kt | 12 ++++++---
.../tusky/adapter/StatusBaseViewHolder.java | 16 ++++++++----
.../com/keylesspalace/tusky/entity/Poll.kt | 8 +++++-
.../tusky/util/NotificationHelper.java | 2 +-
.../tusky/util/StatusViewHelper.kt | 22 +++++++++-------
.../tusky/viewdata/PollViewData.kt | 25 +++++++++++--------
app/src/main/res/values/strings.xml | 4 +++
.../com/keylesspalace/tusky/FilterTest.kt | 1 +
8 files changed, 60 insertions(+), 30 deletions(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt
index 0adc0c371..ea75c16a9 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt
@@ -26,7 +26,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.util.CustomEmojiHelper
-import com.keylesspalace.tusky.util.HtmlUtils
import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.viewdata.PollOptionViewData
import com.keylesspalace.tusky.viewdata.buildDescription
@@ -36,12 +35,19 @@ class PollAdapter: RecyclerView.Adapter() {
private var pollOptions: List = emptyList()
private var voteCount: Int = 0
+ private var votersCount: Int? = null
private var mode = RESULT
private var emojis: List = emptyList()
- fun setup(options: List, voteCount: Int, emojis: List, mode: Int) {
+ fun setup(
+ options: List,
+ voteCount: Int,
+ votersCount: Int?,
+ emojis: List,
+ mode: Int) {
this.pollOptions = options
this.voteCount = voteCount
+ this.votersCount = votersCount
this.emojis = emojis
this.mode = mode
notifyDataSetChanged()
@@ -71,7 +77,7 @@ class PollAdapter: RecyclerView.Adapter() {
when(mode) {
RESULT -> {
- val percent = calculatePercent(option.votesCount, voteCount)
+ val percent = calculatePercent(option.votesCount, votersCount, voteCount)
val emojifiedPollOptionText = CustomEmojiHelper.emojifyText(buildDescription(option.title, percent, holder.resultTextView.context), emojis, holder.resultTextView)
holder.resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText)
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
index ad0e6573d..71156967b 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
@@ -869,7 +869,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
List options = poll.getOptions();
for (int i = 0; i < args.length; i++) {
if (i < options.size()) {
- int percent = PollViewDataKt.calculatePercent(options.get(i).getVotesCount(), poll.getVotesCount());
+ int percent = PollViewDataKt.calculatePercent(options.get(i).getVotesCount(), poll.getVotersCount(), poll.getVotesCount());
args[i] = buildDescription(options.get(i).getTitle(), percent, context);
} else {
args[i] = "";
@@ -912,12 +912,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
if (expired || poll.getVoted()) {
// no voting possible
- pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), emojis, PollAdapter.RESULT);
+ pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), poll.getVotersCount(), emojis, PollAdapter.RESULT);
pollButton.setVisibility(View.GONE);
} else {
// voting possible
- pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), emojis, poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE);
+ pollAdapter.setup(poll.getOptions(), poll.getVotesCount(), poll.getVotersCount(), emojis, poll.getMultiple() ? PollAdapter.MULTIPLE : PollAdapter.SINGLE);
pollButton.setVisibility(View.VISIBLE);
@@ -944,8 +944,14 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private CharSequence getPollInfoText(long timestamp, PollViewData poll,
StatusDisplayOptions statusDisplayOptions,
Context context) {
- String votes = numberFormat.format(poll.getVotesCount());
- String votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), votes);
+ String votesText;
+ if(poll.getVotersCount() == null) {
+ String voters = numberFormat.format(poll.getVotesCount());
+ votesText = context.getResources().getQuantityString(R.plurals.poll_info_votes, poll.getVotesCount(), voters);
+ } else {
+ String voters = numberFormat.format(poll.getVotersCount());
+ votesText = context.getResources().getQuantityString(R.plurals.poll_info_people, poll.getVotersCount(), voters);
+ }
CharSequence pollDurationInfo;
if (poll.getExpired()) {
pollDurationInfo = context.getString(R.string.poll_info_closed);
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt
index 57361d1b0..02c236c48 100644
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Poll.kt
@@ -9,6 +9,7 @@ data class Poll(
val expired: Boolean,
val multiple: Boolean,
@SerializedName("votes_count") val votesCount: Int,
+ @SerializedName("voters_count") val votersCount: Int?, // nullable for compatibility with Pleroma
val options: List,
val voted: Boolean
) {
@@ -22,7 +23,12 @@ data class Poll(
}
}
- return copy(options = newOptions, votesCount = votesCount + choices.size, voted = true)
+ return copy(
+ options = newOptions,
+ votesCount = votesCount + choices.size,
+ votersCount = votersCount?.plus(1),
+ voted = true
+ )
}
fun toNewPoll(creationDate: Date) = NewPoll(
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
index 8b526dc57..c98e8b5ca 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java
@@ -643,7 +643,7 @@ public class NotificationHelper {
Poll poll = notification.getStatus().getPoll();
for(PollOption option: poll.getOptions()) {
builder.append(buildDescription(option.getTitle(),
- PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotesCount()),
+ PollViewDataKt.calculatePercent(option.getVotesCount(), poll.getVotersCount(), poll.getVotesCount()),
context));
builder.append('\n');
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
index 840e220e4..98660bc39 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
@@ -24,7 +24,6 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
-import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Attachment
@@ -275,17 +274,22 @@ class StatusViewHelper(private val itemView: View) {
private fun getPollInfoText(timestamp: Long, poll: PollViewData, pollDescription: TextView, useAbsoluteTime: Boolean): CharSequence {
val context = pollDescription.context
- val votes = NumberFormat.getNumberInstance().format(poll.votesCount.toLong())
- val votesText = context.resources.getQuantityString(R.plurals.poll_info_votes, poll.votesCount, votes)
- val pollDurationInfo: CharSequence
- if (poll.expired) {
- pollDurationInfo = context.getString(R.string.poll_info_closed)
+
+ val votesText = if(poll.votersCount == null) {
+ val votes = NumberFormat.getNumberInstance().format(poll.votesCount.toLong())
+ context.resources.getQuantityString(R.plurals.poll_info_votes, poll.votesCount, votes)
+ } else {
+ val votes = NumberFormat.getNumberInstance().format(poll.votersCount.toLong())
+ context.resources.getQuantityString(R.plurals.poll_info_people, poll.votersCount, votes)
+ }
+ val pollDurationInfo = if (poll.expired) {
+ context.getString(R.string.poll_info_closed)
} else {
if (useAbsoluteTime) {
- pollDurationInfo = context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.expiresAt))
+ context.getString(R.string.poll_info_time_absolute, getAbsoluteTime(poll.expiresAt))
} else {
val pollDuration = TimestampUtils.formatPollDuration(context, poll.expiresAt!!.time, timestamp)
- pollDurationInfo = context.getString(R.string.poll_info_time_relative, pollDuration)
+ context.getString(R.string.poll_info_time_relative, pollDuration)
}
}
@@ -298,7 +302,7 @@ class StatusViewHelper(private val itemView: View) {
for (i in 0 until Status.MAX_POLL_OPTIONS) {
if (i < options.size) {
- val percent = calculatePercent(options[i].votesCount, poll.votesCount)
+ val percent = calculatePercent(options[i].votesCount, poll.votersCount, poll.votesCount)
val pollOptionText = buildDescription(options[i].title, percent, pollResults[i].context)
pollResults[i].text = CustomEmojiHelper.emojifyText(pollOptionText, emojis, pollResults[i])
diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
index dff0c9f60..729893215 100644
--- a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
@@ -31,6 +31,7 @@ data class PollViewData(
val expired: Boolean,
val multiple: Boolean,
val votesCount: Int,
+ val votersCount: Int?,
val options: List,
var voted: Boolean
)
@@ -41,10 +42,11 @@ data class PollOptionViewData(
var selected: Boolean
)
-fun calculatePercent(fraction: Int, total: Int): Int {
+fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int {
return if (fraction == 0) {
0
} else {
+ val total = totalVoters ?: totalVotes
(fraction / total.toDouble() * 100).roundToInt()
}
}
@@ -58,20 +60,21 @@ fun buildDescription(title: String, percent: Int, context: Context): Spanned {
fun Poll?.toViewData(): PollViewData? {
if (this == null) return null
return PollViewData(
- id,
- expiresAt,
- expired,
- multiple,
- votesCount,
- options.map { it.toViewData() },
- voted
+ id = id,
+ expiresAt = expiresAt,
+ expired = expired,
+ multiple = multiple,
+ votesCount = votesCount,
+ votersCount = votersCount,
+ options = options.map { it.toViewData() },
+ voted = voted
)
}
fun PollOption.toViewData(): PollOptionViewData {
return PollOptionViewData(
- title,
- votesCount,
- false
+ title = title,
+ votesCount = votesCount,
+ selected = false
)
}
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b517db86e..509005654 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -494,6 +494,10 @@
- %s vote
- %s votes
+
+ - %s person
+ - %s people
+
%s left
ends at %s
closed
diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
index 19288499f..8b0c916aa 100644
--- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
@@ -222,6 +222,7 @@ class FilterTest {
expired = false,
multiple = false,
votesCount = 0,
+ votersCount = 0,
options = pollOptions.map {
PollOption(it, 0)
},
From adac296e04c9aaf48ef0607d644bfddc1c926df5 Mon Sep 17 00:00:00 2001
From: Konrad Pozniak
Date: Tue, 24 Mar 2020 21:07:10 +0100
Subject: [PATCH 34/42] simplify ComposeOptionsView (#1734)
---
.../compose/view/ComposeOptionsView.kt | 15 +--
app/src/main/res/layout/activity_compose.xml | 8 +-
.../main/res/layout/view_compose_options.xml | 108 +++++++++---------
3 files changed, 59 insertions(+), 72 deletions(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt
index c99df40f2..8f80c76df 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/view/ComposeOptionsView.kt
@@ -17,25 +17,18 @@ package com.keylesspalace.tusky.components.compose.view
import android.content.Context
import android.util.AttributeSet
-import android.widget.LinearLayout
+import android.widget.RadioGroup
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Status
-import kotlinx.android.synthetic.main.view_compose_options.view.*
-
-class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {
+class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : RadioGroup(context, attrs) {
var listener: ComposeOptionsListener? = null
init {
inflate(context, R.layout.view_compose_options, this)
- publicRadioButton.setButtonDrawable(R.drawable.ic_public_24dp)
- unlistedRadioButton.setButtonDrawable(R.drawable.ic_lock_open_24dp)
- privateRadioButton.setButtonDrawable(R.drawable.ic_lock_outline_24dp)
- directRadioButton.setButtonDrawable(R.drawable.ic_email_24dp)
-
- visibilityRadioGroup.setOnCheckedChangeListener { _, checkedId ->
+ setOnCheckedChangeListener { _, checkedId ->
val visibility = when (checkedId) {
R.id.publicRadioButton ->
Status.Visibility.PUBLIC
@@ -67,7 +60,7 @@ class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: Attr
}
- visibilityRadioGroup.check(selectedButton)
+ check(selectedButton)
}
}
diff --git a/app/src/main/res/layout/activity_compose.xml b/app/src/main/res/layout/activity_compose.xml
index c0f934289..d25b2b094 100644
--- a/app/src/main/res/layout/activity_compose.xml
+++ b/app/src/main/res/layout/activity_compose.xml
@@ -215,10 +215,10 @@
android:layout_height="wrap_content"
android:background="?attr/colorSurface"
android:elevation="12dp"
- android:paddingStart="16dp"
- android:paddingTop="8dp"
- android:paddingEnd="16dp"
- android:paddingBottom="52dp"
+ android:paddingStart="24dp"
+ android:paddingTop="12dp"
+ android:paddingEnd="24dp"
+ android:paddingBottom="60dp"
app:behavior_hideable="true"
app:behavior_peekHeight="0dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" />
diff --git a/app/src/main/res/layout/view_compose_options.xml b/app/src/main/res/layout/view_compose_options.xml
index 21f4659a9..ee8c70e87 100644
--- a/app/src/main/res/layout/view_compose_options.xml
+++ b/app/src/main/res/layout/view_compose_options.xml
@@ -1,68 +1,62 @@
+ tools:layout_height="wrap_content"
+ tools:layout_width="match_parent"
+ tools:parentTag="RadioGroup">
-
+
+
+ android:layout_marginBottom="4dp"
+ android:layout_weight="1"
+ android:button="@drawable/ic_lock_open_24dp"
+ android:paddingStart="10dp"
+ android:paddingEnd="0dp"
+ android:text="@string/visibility_unlisted"
+ android:textColor="?android:textColorTertiary"
+ app:buttonTint="@color/compound_button_color" />
-
+
-
-
-
-
-
-
-
+
\ No newline at end of file
From 1b476c790a532968d891be3b0687fc39b3a977e9 Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Fri, 27 Mar 2020 19:04:28 +0100
Subject: [PATCH 35/42] Fix NPE in follow requests view (#1739)
Fixes #1738
---
.../keylesspalace/tusky/adapter/FollowRequestViewHolder.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
index d9fd0491e..fb303354e 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/FollowRequestViewHolder.kt
@@ -23,9 +23,9 @@ internal class FollowRequestViewHolder(itemView: View, private val showHeader: B
val emojifiedName: CharSequence = CustomEmojiHelper.emojifyString(wrappedName, account.emojis, itemView)
itemView.displayNameTextView.text = emojifiedName
if (showHeader) {
- itemView.notificationTextView.text = itemView.context.getString(R.string.notification_follow_request_format, emojifiedName)
+ itemView.notificationTextView?.text = itemView.context.getString(R.string.notification_follow_request_format, emojifiedName)
}
- itemView.notificationTextView.visible(showHeader)
+ itemView.notificationTextView?.visible(showHeader)
val format = itemView.context.getString(R.string.status_username_format)
val formattedUsername = String.format(format, account.username)
itemView.usernameTextView.text = formattedUsername
From 0b8efc934925d596f1caf79859f19c9d06d067f8 Mon Sep 17 00:00:00 2001
From: Juanjo Salvador
Date: Mon, 30 Mar 2020 09:42:45 +0000
Subject: [PATCH 36/42] Translated using Weblate (Spanish)
Currently translated at 100.0% (410 of 410 strings)
Translation: Tusky/Tusky
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/es/
---
app/src/main/res/values-es/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index f0629173d..0084df92f 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -4,7 +4,7 @@
¡Se ha producido un error de red! ¡Por favor, comprueba tu conexión e inténtalo de nuevo!
Este campo no puede estar vacío.
Nombre de dominio incorrecto
- Inicio de sesión fallido.
+ Fallo de autenticación con esta instancia.
No se ha encontrado ningún navegador web.
Ocurrió un error de autorización no identificado.
La autorización falló.
From 91263eed8b4b479f3a2d356bdd14d27828e510c2 Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Mon, 30 Mar 2020 21:03:27 +0200
Subject: [PATCH 37/42] Confirm blocks and mutes from timelines (#1740)
* Add preference for confirming blocks and mutes from timelines
Implements #1737
* Apply code review feedback
---
.../keylesspalace/tusky/AccountActivity.kt | 28 +++++++++++++++++--
.../components/search/SearchViewModel.kt | 2 +-
.../fragments/SearchStatusesFragment.kt | 20 +++++++++++--
.../tusky/fragment/SFragment.java | 21 ++++++++++++--
app/src/main/res/values/strings.xml | 2 ++
5 files changed, 66 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
index ad91b19ec..333eb09d1 100644
--- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
@@ -681,6 +681,30 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
.show()
}
+ private fun toggleBlock() {
+ if (viewModel.relationshipData.value?.data?.blocking != true) {
+ AlertDialog.Builder(this)
+ .setMessage(getString(R.string.dialog_block_warning, loadedAccount?.username))
+ .setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeBlockState() }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ } else {
+ viewModel.changeBlockState()
+ }
+ }
+
+ private fun toggleMute() {
+ if (viewModel.relationshipData.value?.data?.muting != true) {
+ AlertDialog.Builder(this)
+ .setMessage(getString(R.string.dialog_mute_warning, loadedAccount?.username))
+ .setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeMuteState() }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ } else {
+ viewModel.changeMuteState()
+ }
+ }
+
private fun mention() {
loadedAccount?.let {
val intent = ComposeActivity.startIntent(this,
@@ -727,11 +751,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
return true
}
R.id.action_block -> {
- viewModel.changeBlockState()
+ toggleBlock()
return true
}
R.id.action_mute -> {
- viewModel.changeMuteState()
+ toggleMute()
return true
}
R.id.action_mute_domain -> {
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt
index 71121a91d..aa55c99cd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt
@@ -193,7 +193,7 @@ class SearchViewModel @Inject constructor(
return accountManager.getAllAccountsOrderedByActive()
}
- fun muteAcount(accountId: String) {
+ fun muteAccount(accountId: String) {
timelineCases.mute(accountId)
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
index 0833f04cd..4e5f64fbe 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
@@ -325,11 +325,11 @@ class SearchStatusesFragment : SearchFragment {
- viewModel.muteAcount(accountId)
+ onMute(accountId, accountUsername)
return@setOnMenuItemClickListener true
}
R.id.status_block -> {
- viewModel.blockAccount(accountId)
+ onBlock(accountId, accountUsername)
return@setOnMenuItemClickListener true
}
R.id.status_report -> {
@@ -362,6 +362,22 @@ class SearchStatusesFragment : SearchFragment viewModel.blockAccount(accountId) }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ }
+
+ private fun onMute(accountId: String, accountUsername: String) {
+ AlertDialog.Builder(requireContext())
+ .setMessage(getString(R.string.dialog_mute_warning, accountUsername))
+ .setPositiveButton(android.R.string.ok) { _, _ -> viewModel.muteAccount(accountId) }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ }
+
private fun accountIsInMentions(account: AccountEntity?, mentions: Array): Boolean {
return mentions.firstOrNull {
account?.username == it.username && account.domain == Uri.parse(it.url)?.host
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
index 6a7e5f12d..5b475a046 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
@@ -39,6 +39,7 @@ import androidx.appcompat.widget.PopupMenu;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.view.ViewCompat;
import androidx.lifecycle.Lifecycle;
+import androidx.preference.PreferenceManager;
import com.keylesspalace.tusky.BaseActivity;
import com.keylesspalace.tusky.BottomSheetActivity;
@@ -284,11 +285,11 @@ public abstract class SFragment extends BaseFragment implements Injectable {
return true;
}
case R.id.status_mute: {
- timelineCases.mute(accountId);
+ onMute(accountId, accountUsername);
return true;
}
case R.id.status_block: {
- timelineCases.block(accountId);
+ onBlock(accountId, accountUsername);
return true;
}
case R.id.status_report: {
@@ -328,6 +329,22 @@ public abstract class SFragment extends BaseFragment implements Injectable {
popup.show();
}
+ private void onMute(String accountId, String accountUsername) {
+ new AlertDialog.Builder(requireContext())
+ .setMessage(getString(R.string.dialog_mute_warning, accountUsername))
+ .setPositiveButton(android.R.string.ok, (__, ___) -> timelineCases.mute(accountId))
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ private void onBlock(String accountId, String accountUsername) {
+ new AlertDialog.Builder(requireContext())
+ .setMessage(getString(R.string.dialog_block_warning, accountUsername))
+ .setPositiveButton(android.R.string.ok, (__, ___) -> timelineCases.block(accountId))
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
private static boolean accountIsInMentions(AccountEntity account, Status.Mention[] mentions) {
if (account == null) {
return false;
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 509005654..bf480226f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -196,6 +196,8 @@
Delete and re-draft this toot?
Are you sure you want to block all of %s? You will not see content from that domain in any public timelines or in your notifications. Your followers from that domain will be removed.
Hide entire domain
+ Block @%s?
+ Mute @%s?
Public: Post to public timelines
Unlisted: Do not show in public timelines
From 914eff95fc618e62db065c20a8d1ff0dcec9fef3 Mon Sep 17 00:00:00 2001
From: Jordi Brunet
Date: Sat, 21 Mar 2020 20:53:32 +0000
Subject: [PATCH 38/42] Translated using Weblate (Catalan)
Currently translated at 100.0% (8 of 8 strings)
Translation: Tusky/Tusky-app
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/ca/
---
fastlane/metadata/android/ca/short_description.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fastlane/metadata/android/ca/short_description.txt b/fastlane/metadata/android/ca/short_description.txt
index c24acd732..151f74e27 100644
--- a/fastlane/metadata/android/ca/short_description.txt
+++ b/fastlane/metadata/android/ca/short_description.txt
@@ -1 +1 @@
-Un client per a diversos comptes per a la xarxa social Mastodont
+Un client multicomptes per a la xarxa social Mastodont
From 68f34152dce790e7000b37ae50aa7bc93d96e48f Mon Sep 17 00:00:00 2001
From: Konrad Pozniak
Date: Thu, 2 Apr 2020 23:37:38 +0200
Subject: [PATCH 39/42] replace HtmlUtils with HtmlCompat (#1741)
* replace HtmlUtils with HtmlCompat
* fix tests
---
.../tusky/adapter/StatusBaseViewHolder.java | 6 +--
.../com/keylesspalace/tusky/db/Converters.kt | 7 +--
.../com/keylesspalace/tusky/di/AppModule.kt | 7 ---
.../tusky/di/RepositoryModule.kt | 13 ++---
.../com/keylesspalace/tusky/entity/Account.kt | 33 +++---------
.../com/keylesspalace/tusky/entity/Card.kt | 10 ++--
.../tusky/json/SpannedTypeAdapter.java | 17 ++++--
.../tusky/repository/TimelineRepository.kt | 17 +++---
.../keylesspalace/tusky/util/HtmlConverter.kt | 22 --------
.../keylesspalace/tusky/util/HtmlUtils.java | 52 -------------------
.../tusky/util/StatusViewHelper.kt | 5 +-
.../tusky/viewdata/PollViewData.kt | 4 +-
.../tusky/fragment/TimelineRepositoryTest.kt | 37 ++++++-------
13 files changed, 65 insertions(+), 165 deletions(-)
delete mode 100644 app/src/main/java/com/keylesspalace/tusky/util/HtmlConverter.kt
delete mode 100644 app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
index 71156967b..f808cb1bb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
@@ -20,6 +20,7 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
+import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -38,7 +39,6 @@ import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.CardViewMode;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
-import com.keylesspalace.tusky.util.HtmlUtils;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.StatusDisplayOptions;
@@ -884,7 +884,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
protected CharSequence getFavsText(Context context, int count) {
if (count > 0) {
String countString = numberFormat.format(count);
- return HtmlUtils.fromHtml(context.getResources().getQuantityString(R.plurals.favs, count, countString));
+ return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.favs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
} else {
return "";
}
@@ -893,7 +893,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
protected CharSequence getReblogsText(Context context, int count) {
if (count > 0) {
String countString = numberFormat.format(count);
- return HtmlUtils.fromHtml(context.getResources().getQuantityString(R.plurals.reblogs, count, countString));
+ return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.reblogs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
} else {
return "";
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt
index 0074b8d9b..09f8a5cbd 100644
--- a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt
@@ -16,6 +16,8 @@
package com.keylesspalace.tusky.db
import android.text.Spanned
+import androidx.core.text.parseAsHtml
+import androidx.core.text.toHtml
import androidx.room.TypeConverter
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
@@ -27,7 +29,6 @@ import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.json.SpannedTypeAdapter
-import com.keylesspalace.tusky.util.HtmlUtils
import java.net.URLDecoder
import java.net.URLEncoder
import java.util.*
@@ -128,7 +129,7 @@ class Converters {
if(spanned == null) {
return null
}
- return HtmlUtils.toHtml(spanned)
+ return spanned.toHtml()
}
@TypeConverter
@@ -136,7 +137,7 @@ class Converters {
if(spannedString == null) {
return null
}
- return HtmlUtils.fromHtml(spannedString)
+ return spannedString.parseAsHtml()
}
@TypeConverter
diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
index a4f2eb5eb..e87ef2894 100644
--- a/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/di/AppModule.kt
@@ -29,8 +29,6 @@ import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.network.TimelineCases
import com.keylesspalace.tusky.network.TimelineCasesImpl
-import com.keylesspalace.tusky.util.HtmlConverter
-import com.keylesspalace.tusky.util.HtmlConverterImpl
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@@ -84,9 +82,4 @@ class AppModule {
.build()
}
- @Provides
- @Singleton
- fun providesHtmlConverter(): HtmlConverter {
- return HtmlConverterImpl()
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/di/RepositoryModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/RepositoryModule.kt
index 1b5206a74..af8cfb886 100644
--- a/app/src/main/java/com/keylesspalace/tusky/di/RepositoryModule.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/di/RepositoryModule.kt
@@ -6,17 +6,18 @@ import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.repository.TimelineRepository
import com.keylesspalace.tusky.repository.TimelineRepositoryImpl
-import com.keylesspalace.tusky.util.HtmlConverter
import dagger.Module
import dagger.Provides
@Module
class RepositoryModule {
@Provides
- fun providesTimelineRepository(db: AppDatabase, mastodonApi: MastodonApi,
- accountManager: AccountManager, gson: Gson,
- htmlConverter: HtmlConverter): TimelineRepository {
- return TimelineRepositoryImpl(db.timelineDao(), mastodonApi, accountManager, gson,
- htmlConverter)
+ fun providesTimelineRepository(
+ db: AppDatabase,
+ mastodonApi: MastodonApi,
+ accountManager: AccountManager,
+ gson: Gson
+ ): TimelineRepository {
+ return TimelineRepositoryImpl(db.timelineDao(), mastodonApi, accountManager, gson)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt
index 62990758e..d4940fe0e 100644
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Account.kt
@@ -15,24 +15,16 @@
package com.keylesspalace.tusky.entity
-import android.os.Parcel
-import android.os.Parcelable
import android.text.Spanned
-
import com.google.gson.annotations.SerializedName
-import com.keylesspalace.tusky.util.HtmlUtils
-import kotlinx.android.parcel.Parceler
-import kotlinx.android.parcel.Parcelize
-import kotlinx.android.parcel.WriteWith
-import java.util.*
+import java.util.Date
-@Parcelize
data class Account(
val id: String,
@SerializedName("username") val localUsername: String,
@SerializedName("acct") val username: String,
@SerializedName("display_name") val displayName: String?, // should never be null per Api definition, but some servers break the contract
- val note: @WriteWith() Spanned,
+ val note: Spanned,
val url: String,
val avatar: String,
val header: String,
@@ -46,7 +38,7 @@ data class Account(
val fields: List? = emptyList(), //nullable for backward compatibility
val moved: Account? = null
-) : Parcelable {
+) {
val name: String
get() = if (displayName.isNullOrEmpty()) {
@@ -86,31 +78,20 @@ data class Account(
fun isRemote(): Boolean = this.username != this.localUsername
}
-@Parcelize
data class AccountSource(
val privacy: Status.Visibility,
val sensitive: Boolean,
val note: String,
val fields: List?
-): Parcelable
+)
-@Parcelize
data class Field (
val name: String,
- val value: @WriteWith() Spanned,
+ val value: Spanned,
@SerializedName("verified_at") val verifiedAt: Date?
-): Parcelable
+)
-@Parcelize
data class StringField (
val name: String,
val value: String
-): Parcelable
-
-object SpannedParceler : Parceler {
- override fun create(parcel: Parcel): Spanned = HtmlUtils.fromHtml(parcel.readString())
-
- override fun Spanned.write(parcel: Parcel, flags: Int) {
- parcel.writeString(HtmlUtils.toHtml(this))
- }
-}
\ No newline at end of file
+)
diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt
index c6f82637f..43e54f010 100644
--- a/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt
@@ -15,23 +15,19 @@
package com.keylesspalace.tusky.entity
-import android.os.Parcelable
import android.text.Spanned
import com.google.gson.annotations.SerializedName
-import kotlinx.android.parcel.Parcelize
-import kotlinx.android.parcel.WriteWith
-@Parcelize
data class Card(
val url: String,
- val title: @WriteWith() Spanned,
- val description: @WriteWith() Spanned,
+ val title: Spanned,
+ val description: Spanned,
@SerializedName("author_name") val authorName: String,
val image: String,
val type: String,
val width: Int,
val height: Int
-) : Parcelable {
+) {
override fun hashCode(): Int {
return url.hashCode()
diff --git a/app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java b/app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java
index cabd3eb86..a884695f8 100644
--- a/app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java
+++ b/app/src/main/java/com/keylesspalace/tusky/json/SpannedTypeAdapter.java
@@ -18,6 +18,8 @@ package com.keylesspalace.tusky.json;
import android.text.Spanned;
import android.text.SpannedString;
+import androidx.core.text.HtmlCompat;
+
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
@@ -25,7 +27,6 @@ import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
-import com.keylesspalace.tusky.util.HtmlUtils;
import java.lang.reflect.Type;
@@ -35,7 +36,9 @@ public class SpannedTypeAdapter implements JsonDeserializer, JsonSerial
throws JsonParseException {
String string = json.getAsString();
if (string != null) {
- return HtmlUtils.fromHtml(string);
+ /* Html.fromHtml returns trailing whitespace if the html ends in a
tag, which
+ * all status contents do, so it should be trimmed. */
+ return (Spanned)trimTrailingWhitespace(HtmlCompat.fromHtml(string, HtmlCompat.FROM_HTML_MODE_LEGACY));
} else {
return new SpannedString("");
}
@@ -43,6 +46,14 @@ public class SpannedTypeAdapter implements JsonDeserializer, JsonSerial
@Override
public JsonElement serialize(Spanned src, Type typeOfSrc, JsonSerializationContext context) {
- return new JsonPrimitive(HtmlUtils.toHtml(src));
+ return new JsonPrimitive(HtmlCompat.toHtml(src, HtmlCompat.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL));
+ }
+
+ private static CharSequence trimTrailingWhitespace(CharSequence s) {
+ int i = s.length();
+ do {
+ i--;
+ } while (i >= 0 && Character.isWhitespace(s.charAt(i)));
+ return s.subSequence(0, i + 1);
}
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt b/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt
index f16bb542c..0023a9f88 100644
--- a/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/repository/TimelineRepository.kt
@@ -1,6 +1,8 @@
package com.keylesspalace.tusky.repository
import android.text.SpannedString
+import androidx.core.text.parseAsHtml
+import androidx.core.text.toHtml
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.keylesspalace.tusky.db.*
@@ -9,7 +11,6 @@ import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.repository.TimelineRequestMode.DISK
import com.keylesspalace.tusky.repository.TimelineRequestMode.NETWORK
import com.keylesspalace.tusky.util.Either
-import com.keylesspalace.tusky.util.HtmlConverter
import com.keylesspalace.tusky.util.dec
import com.keylesspalace.tusky.util.inc
import io.reactivex.Single
@@ -40,8 +41,7 @@ class TimelineRepositoryImpl(
private val timelineDao: TimelineDao,
private val mastodonApi: MastodonApi,
private val accountManager: AccountManager,
- private val gson: Gson,
- private val htmlConverter: HtmlConverter
+ private val gson: Gson
) : TimelineRepository {
init {
@@ -150,7 +150,7 @@ class TimelineRepositoryImpl(
for (status in statuses) {
timelineDao.insertInTransaction(
- status.toEntity(accountId, htmlConverter, gson),
+ status.toEntity(accountId, gson),
status.account.toEntity(accountId, gson),
status.reblog?.account?.toEntity(accountId, gson)
)
@@ -214,7 +214,7 @@ class TimelineRepositoryImpl(
inReplyToId = status.inReplyToId,
inReplyToAccountId = status.inReplyToAccountId,
reblog = null,
- content = status.content?.let(htmlConverter::fromHtml) ?: SpannedString(""),
+ content = status.content?.parseAsHtml() ?: SpannedString(""),
createdAt = Date(status.createdAt),
emojis = emojis,
reblogsCount = status.reblogsCount,
@@ -269,7 +269,7 @@ class TimelineRepositoryImpl(
inReplyToId = status.inReplyToId,
inReplyToAccountId = status.inReplyToAccountId,
reblog = null,
- content = status.content?.let(htmlConverter::fromHtml) ?: SpannedString(""),
+ content = status.content?.parseAsHtml() ?: SpannedString(""),
createdAt = Date(status.createdAt),
emojis = emojis,
reblogsCount = status.reblogsCount,
@@ -362,7 +362,6 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
}
fun Status.toEntity(timelineUserId: Long,
- htmlConverter: HtmlConverter,
gson: Gson): TimelineStatusEntity {
val actionable = actionableStatus
return TimelineStatusEntity(
@@ -372,7 +371,7 @@ fun Status.toEntity(timelineUserId: Long,
authorServerId = actionable.account.id,
inReplyToId = actionable.inReplyToId,
inReplyToAccountId = actionable.inReplyToAccountId,
- content = htmlConverter.toHtml(actionable.content),
+ content = actionable.content.toHtml(),
createdAt = actionable.createdAt.time,
emojis = actionable.emojis.let(gson::toJson),
reblogsCount = actionable.reblogsCount,
@@ -385,7 +384,7 @@ fun Status.toEntity(timelineUserId: Long,
visibility = actionable.visibility,
attachments = actionable.attachments.let(gson::toJson),
mentions = actionable.mentions.let(gson::toJson),
- application = actionable.let(gson::toJson),
+ application = actionable.application.let(gson::toJson),
reblogServerId = reblog?.id,
reblogAccountId = reblog?.let { this.account.id },
poll = actionable.poll.let(gson::toJson),
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/HtmlConverter.kt b/app/src/main/java/com/keylesspalace/tusky/util/HtmlConverter.kt
deleted file mode 100644
index 72efc28c6..000000000
--- a/app/src/main/java/com/keylesspalace/tusky/util/HtmlConverter.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.keylesspalace.tusky.util
-
-import android.text.Spanned
-
-/**
- * Abstracting away Android-specific things.
- */
-interface HtmlConverter {
- fun fromHtml(html: String): Spanned
-
- fun toHtml(text: Spanned): String
-}
-
-internal class HtmlConverterImpl : HtmlConverter {
- override fun fromHtml(html: String): Spanned {
- return HtmlUtils.fromHtml(html)
- }
-
- override fun toHtml(text: Spanned): String {
- return HtmlUtils.toHtml(text)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java
deleted file mode 100644
index cba8f2fab..000000000
--- a/app/src/main/java/com/keylesspalace/tusky/util/HtmlUtils.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright 2017 Andrew Dawson
- *
- * This file is a part of Tusky.
- *
- * This program is free software; you can redistribute it and/or modify it under the terms of the
- * GNU General Public License as published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
- * Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with Tusky; if not,
- * see . */
-
-package com.keylesspalace.tusky.util;
-
-import android.os.Build;
-import android.text.Html;
-import android.text.Spanned;
-
-public class HtmlUtils {
- private static CharSequence trimTrailingWhitespace(CharSequence s) {
- int i = s.length();
- do {
- i--;
- } while (i >= 0 && Character.isWhitespace(s.charAt(i)));
- return s.subSequence(0, i + 1);
- }
-
- public static Spanned fromHtml(String html) {
- Spanned result;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
- } else {
- result = Html.fromHtml(html);
- }
- /* Html.fromHtml returns trailing whitespace if the html ends in a tag, which
- * all status contents do, so it should be trimmed. */
- return (Spanned) trimTrailingWhitespace(result);
- }
-
- public static String toHtml(Spanned text) {
- String result;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- result = Html.toHtml(text, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
- } else {
- result = Html.toHtml(text);
- }
- return result;
- }
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
index 98660bc39..79c0c4b84 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
@@ -171,15 +171,12 @@ class StatusViewHelper(private val itemView: View) {
sensitiveMediaWarning.visibility = View.GONE
sensitiveMediaShow.visibility = View.GONE
} else {
-
- val hiddenContentText: String = if (sensitive) {
+ sensitiveMediaWarning.text = if (sensitive) {
context.getString(R.string.status_sensitive_media_title)
} else {
context.getString(R.string.status_media_hidden_title)
}
- sensitiveMediaWarning.text = HtmlUtils.fromHtml(hiddenContentText)
-
sensitiveMediaWarning.visibility = if (showingContent) View.GONE else View.VISIBLE
sensitiveMediaShow.visibility = if (showingContent) View.VISIBLE else View.GONE
sensitiveMediaShow.setOnClickListener { v ->
diff --git a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
index 729893215..b6eefd713 100644
--- a/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/viewdata/PollViewData.kt
@@ -18,10 +18,10 @@ package com.keylesspalace.tusky.viewdata
import android.content.Context
import android.text.SpannableStringBuilder
import android.text.Spanned
+import androidx.core.text.parseAsHtml
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.PollOption
-import com.keylesspalace.tusky.util.HtmlUtils
import java.util.*
import kotlin.math.roundToInt
@@ -52,7 +52,7 @@ fun calculatePercent(fraction: Int, totalVoters: Int?, totalVotes: Int): Int {
}
fun buildDescription(title: String, percent: Int, context: Context): Spanned {
- return SpannableStringBuilder(HtmlUtils.fromHtml(context.getString(R.string.poll_percent_format, percent)))
+ return SpannableStringBuilder(context.getString(R.string.poll_percent_format, percent).parseAsHtml())
.append(" ")
.append(title)
}
diff --git a/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt b/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt
index f80f57b8c..a3db3b448 100644
--- a/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/fragment/TimelineRepositoryTest.kt
@@ -1,6 +1,8 @@
package com.keylesspalace.tusky.fragment
-import android.text.Spanned
+import android.text.SpannableString
+import android.text.SpannedString
+import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.gson.Gson
import com.keylesspalace.tusky.SpanUtilsTest
import com.keylesspalace.tusky.db.AccountEntity
@@ -12,7 +14,6 @@ import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.repository.*
import com.keylesspalace.tusky.util.Either
-import com.keylesspalace.tusky.util.HtmlConverter
import com.nhaarman.mockitokotlin2.isNull
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
@@ -24,14 +25,18 @@ import io.reactivex.schedulers.TestScheduler
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import org.robolectric.annotation.Config
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList
+@Config(sdk = [28])
+@RunWith(AndroidJUnit4::class)
class TimelineRepositoryTest {
@Mock
lateinit var timelineDao: TimelineDao
@@ -56,15 +61,6 @@ class TimelineRepositoryTest {
domain = "domain.com",
isActive = true
)
- private val htmlConverter = object : HtmlConverter {
- override fun fromHtml(html: String): Spanned {
- return SpanUtilsTest.FakeSpannable(html)
- }
-
- override fun toHtml(text: Spanned): String {
- return text.toString()
- }
- }
@Before
fun setup() {
@@ -74,8 +70,7 @@ class TimelineRepositoryTest {
gson = Gson()
testScheduler = TestScheduler()
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
- subject = TimelineRepositoryImpl(timelineDao, mastodonApi, accountManager, gson,
- htmlConverter)
+ subject = TimelineRepositoryImpl(timelineDao, mastodonApi, accountManager, gson)
}
@Test
@@ -97,7 +92,7 @@ class TimelineRepositoryTest {
verify(timelineDao).insertStatusIfNotThere(Placeholder("1").toEntity(account.id))
for (status in statuses) {
verify(timelineDao).insertInTransaction(
- status.toEntity(account.id, htmlConverter, gson),
+ status.toEntity(account.id, gson),
status.account.toEntity(account.id, gson),
null
)
@@ -129,7 +124,7 @@ class TimelineRepositoryTest {
// We assume for now that overlapped one is inserted but it's not that important
for (status in response) {
verify(timelineDao).insertInTransaction(
- status.toEntity(account.id, htmlConverter, gson),
+ status.toEntity(account.id, gson),
status.account.toEntity(account.id, gson),
null
)
@@ -159,7 +154,7 @@ class TimelineRepositoryTest {
verify(timelineDao).deleteRange(account.id, response.last().id, response.first().id)
for (status in response) {
verify(timelineDao).insertInTransaction(
- status.toEntity(account.id, htmlConverter, gson),
+ status.toEntity(account.id, gson),
status.account.toEntity(account.id, gson),
null
)
@@ -201,7 +196,7 @@ class TimelineRepositoryTest {
// We assume for now that overlapped one is inserted but it's not that important
for (status in response) {
verify(timelineDao).insertInTransaction(
- status.toEntity(account.id, htmlConverter, gson),
+ status.toEntity(account.id, gson),
status.account.toEntity(account.id, gson),
null
)
@@ -246,7 +241,7 @@ class TimelineRepositoryTest {
for (status in response) {
verify(timelineDao).insertInTransaction(
- status.toEntity(account.id, htmlConverter, gson),
+ status.toEntity(account.id, gson),
status.account.toEntity(account.id, gson),
null
)
@@ -263,7 +258,7 @@ class TimelineRepositoryTest {
val status = makeStatus("2")
val dbStatus = makeStatus("1")
val dbResult = TimelineStatusWithAccount()
- dbResult.status = dbStatus.toEntity(account.id, htmlConverter, gson)
+ dbResult.status = dbStatus.toEntity(account.id, gson)
dbResult.account = status.account.toEntity(account.id, gson)
whenever(mastodonApi.homeTimelineSingle(any(), any(), any()))
@@ -297,7 +292,7 @@ class TimelineRepositoryTest {
return Status(
id = id,
account = account,
- content = SpanUtilsTest.FakeSpannable("hello$id"),
+ content = SpannableString("hello$id"),
createdAt = Date(),
emojis = listOf(),
reblogsCount = 3,
@@ -328,7 +323,7 @@ class TimelineRepositoryTest {
localUsername = "test$id",
username = "test$id@example.com",
displayName = "Example Account $id",
- note = SpanUtilsTest.FakeSpannable("Note! $id"),
+ note = SpannableString("Note! $id"),
url = "https://example.com/@test$id",
avatar = "avatar$id",
header = "Header$id",
From a5416abb21ac7007cb6e634c4ac96a06ec94da3f Mon Sep 17 00:00:00 2001
From: Levi Bard
Date: Fri, 3 Apr 2020 19:04:06 +0200
Subject: [PATCH 40/42] Add missing error handler for mute conversation
invocation (#1746)
---
.../main/java/com/keylesspalace/tusky/fragment/SFragment.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
index 5b475a046..935a02e7b 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java
@@ -318,6 +318,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
}
case R.id.status_mute_conversation: {
timelineCases.muteConversation(status, status.getMuted() == null || !status.getMuted())
+ .onErrorReturnItem(status)
.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe();
From c80fa68dbeb0fa4292139156fb6e03d11c014531 Mon Sep 17 00:00:00 2001
From: Konrad Pozniak
Date: Mon, 6 Apr 2020 11:46:38 +0200
Subject: [PATCH 41/42] upgrade dependencies, fix some warnings (#1747)
* upgrade dependencies, fix some warnings
* fix tests
---
app/build.gradle | 20 +++++++++---------
.../res/mipmap-hdpi/ic_shortcut_compose.png | Bin 4047 -> 0 bytes
.../res/mipmap-mdpi/ic_shortcut_compose.png | Bin 2352 -> 0 bytes
.../res/mipmap-xhdpi/ic_shortcut_compose.png | Bin 6118 -> 0 bytes
.../res/mipmap-xxhdpi/ic_shortcut_compose.png | Bin 10037 -> 0 bytes
.../mipmap-xxxhdpi/ic_shortcut_compose.png | Bin 12931 -> 0 bytes
.../keylesspalace/tusky/AccountActivity.kt | 8 +++----
.../tusky/AccountsInListFragment.kt | 8 +++----
.../com/keylesspalace/tusky/ListsActivity.kt | 2 +-
.../tusky/TabPreferenceActivity.kt | 2 +-
.../tusky/fragment/AccountMediaFragment.kt | 8 +++----
.../tusky/fragment/TimelineFragment.java | 2 +-
.../tusky/fragment/ViewImageFragment.kt | 4 ++--
.../tusky/fragment/ViewVideoFragment.kt | 6 +++---
app/src/main/res/values/dimens.xml | 1 -
.../com/keylesspalace/tusky/FilterTest.kt | 5 +++++
.../keylesspalace/tusky/TuskyApplication.kt | 4 ----
build.gradle | 4 ++--
gradle/wrapper/gradle-wrapper.properties | 2 +-
gradlew.bat | 3 +++
20 files changed, 40 insertions(+), 39 deletions(-)
delete mode 100644 app/src/blue/res/mipmap-hdpi/ic_shortcut_compose.png
delete mode 100644 app/src/blue/res/mipmap-mdpi/ic_shortcut_compose.png
delete mode 100644 app/src/blue/res/mipmap-xhdpi/ic_shortcut_compose.png
delete mode 100644 app/src/blue/res/mipmap-xxhdpi/ic_shortcut_compose.png
delete mode 100644 app/src/blue/res/mipmap-xxxhdpi/ic_shortcut_compose.png
diff --git a/app/build.gradle b/app/build.gradle
index 1defe9f78..8e3931eb2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -96,23 +96,23 @@ project.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
}
ext.lifecycleVersion = "2.2.0"
-ext.roomVersion = '2.2.4'
-ext.retrofitVersion = '2.7.1'
-ext.okhttpVersion = '4.3.1'
-ext.glideVersion = '4.10.0'
-ext.daggerVersion = '2.26'
+ext.roomVersion = '2.2.5'
+ext.retrofitVersion = '2.8.1'
+ext.okhttpVersion = '4.4.0'
+ext.glideVersion = '4.11.0'
+ext.daggerVersion = '2.27'
// if libraries are changed here, they should also be changed in LicenseActivity
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0"
- implementation "androidx.appcompat:appcompat:1.2.0-alpha02"
- implementation "androidx.fragment:fragment-ktx:1.2.2"
+ implementation "androidx.appcompat:appcompat:1.2.0-beta01"
+ implementation "androidx.fragment:fragment-ktx:1.2.4"
implementation "androidx.browser:browser:1.2.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation "androidx.recyclerview:recyclerview:1.1.0"
- implementation "androidx.exifinterface:exifinterface:1.1.0"
+ implementation "androidx.exifinterface:exifinterface:1.2.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.sharetarget:sharetarget:1.0.0-rc01"
@@ -122,7 +122,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:$lifecycleVersion"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
- implementation "androidx.paging:paging-runtime-ktx:2.1.1"
+ implementation "androidx.paging:paging-runtime-ktx:2.1.2"
implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.room:room-runtime:$roomVersion"
implementation "androidx.room:room-rxjava2:$roomVersion"
@@ -137,7 +137,7 @@ dependencies {
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
- implementation "org.conscrypt:conscrypt-android:2.2.1"
+ implementation "org.conscrypt:conscrypt-android:2.4.0"
implementation "com.github.bumptech.glide:glide:$glideVersion"
implementation "com.github.bumptech.glide:okhttp3-integration:$glideVersion"
diff --git a/app/src/blue/res/mipmap-hdpi/ic_shortcut_compose.png b/app/src/blue/res/mipmap-hdpi/ic_shortcut_compose.png
deleted file mode 100644
index 03ac90029135cae6aebe5cb339f2f49e83e4d5af..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 4047
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
diff --git a/app/src/blue/res/mipmap-xxhdpi/ic_shortcut_compose.png b/app/src/blue/res/mipmap-xxhdpi/ic_shortcut_compose.png
deleted file mode 100644
index 1ad9ff5ae3829f54a01e22184fd4ecd8fa93f5d1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 10037
zcmV-5C(77~P)(JKm@{WSd$>4AQlyrO$oB&Zm|d?0l!bhB4u$w
z7A;B_eTrH?ZA;tlYZZ&uR;`MVg!9ciGw0rO&YhVvbMDP0VD9tG6CQH6bLN--`+whe
z&KZ}h;nDDDcr-j39u1F%$Hn{58o8D)Z;VILb4`Rpe;&N1;qe261VeBn0T1|hR3M<&
zTHmh$u1if-bWp_4_04!XGk4ZAfxKIHw#+YmrbE-Rs&4tERXt&le?QX!eh1Hi=h{LN
z0;>V2iwhb()G!{w>0AB5**p8>mF<|4Kf7vqe%X#~!Lq8?^2(}CIwL4_zpyc8epnl4?R2?9uQjcW>;R16k;Qq$hWCt
zut2E|mQ_~6$SbX^A)}&}jLP%k5qcheUHE;_|FOY3gTD)WC-mJ&_%j}d9_AVz7e;N+
z1O#SRUV}o(V@PFAMgT)6ISdH~L93&2UWY66nHH6T?+D*DSh{nt=?|bmmFoCmIk{kH
z(O>mqG%4?!pee07kNpZ!h`NMD0V*9R_--bk-p9v)jwRxcq~TFF;N;KVF(9vWM
z-ni+FollkRrFrh@yo?M&
ziVckw7|x{6`LEaw0a#q1>|wHows%Zn?Fa9TN;fBl((>6=_nW~HXH;@ZQ-{DP17%ew
z6_6_QRth;k=v=YR;SaAMh#rAim95c9dEKHq3g9>d=GOm??N9>B2$MWW7BzYu&K;}N
zap|`pbfa9deH=PrU&@3lX>b&PX?E+g<}p`cSYE3H6*I&|dL?dQlByA$yTGii<5}nS
zlJ`5ovZ~e2R7U|E5i}7{Jf}GLP)WmN2a=>31xvTDL8r9QrR|GomeslFxg>PZKM$wo
zm9;SihXsr*D7QT?KV;zI0i_619v`HdP|kl-IeczsZT%>B>LYD04JFdyDcRl`9o(s?;NgTSGdLn>B0*^$FUwcQL?tv>wzU&FsL%0%l~taT0Y?ZA)8B}ch6fD;
zjCl$}0F@d{vLGo!G;`}ebEjSg7!6X8S8P+00nh2u6j4h{f$3Jk9g)^E)wwUyU8y&<4F@Z-)al%Stg2oP*`MaYZ
zwm`{(#PCQGYQ%>K{ha>Phrz-{{);2U*9%!i%aelPY)cXz#c(AIQAV{<0p>-8Aq$jM
znXDm>4^q?+(Ldb4^v%C<^(#uP=f%cQDZLT;(!12)+38$$xQZi!hC*|UtBD_E?IOAtpVBGqN^(!-@GzX_W35CKhcniRk!j`cZ
z$jq!5p~?!52^uRf94PMGD>v+*V!>n$ak6R@JBaWCH8fBE^4+xPvU*~w4ZbJ5B%?Zd
zcx+V15je82?6VmvV3}bOgQWW-qZ&m45i7-qxdA_P^P_K}Icsv^{Sc`wGzyk%Um^58
zlG&DXco^WY)J6iBmOrr{Qcy{Ow3`hQOO2Eu+98A}ICG2Uo49T*_Cpt}K15A(VEWc8
zF2VyXIV3|QGxNE<5<*Gy4unkAFE6ZQg3Ks5V7;THejs--#
zP=>NH|G3qQ;~UCIC9_d}@xv{UtN#}XU6Bjmg{GNR=ZS!a?|00q!}2&BaCp$%^^)@;
z1=Bw5vU?6_PaOWEcJlPs+E?G5)&BI4ueC=Hex!9=wBJ6Y(Q<{YCt~!@_z;o1iR2bP
z`fva6+uIR{;!&k0-ls8=z%7nK7pu-Ar93Yh;1R118ytIRTHW=s)R2QI3{vkEuW1KP
ze4^FX)|y65O^tT&{Zm@EB`?viW(N`9SUd7V*ib*M<&Iyw6IZG0Jguk38M*jAp*wi7
zjI*d%XsbLNcx*$K1UNic?lz8BB!=PnJgDer_E>&U+xxpuw6o`GA^?I>jUamd*Z-&W
zUj9lbQwtX%MG!qN36U_A5ko!o@mk-g`{H1ziNt|%-Yus-vcAWVs>cEzJ8+a>c|{E>
z1590(9@Kt;;5k>#hQ|oeo_GGPbzXeH2BH_l5DEQ|K!s*))BF?Gy@YE+br!~S^_j7G
zaA-ligRCnubVUP??sYh-qYBPFugVPxkov4VtnE8-3c*n;ghw9$8HBlB%U%g(Y3y)j
zfJkJjEulV_I%C$5wb);79c#u*u{`*@2sHs#fQq|$a?=o0xJMdeWeCjyAYht!AV
zJvtzO9zJwZIdqA#Go0exPPH#AtI5^{HcXF3%
z@YIJ}^I;SV^%KJ?5{i^Z1s*FnOfU{h3_eU9mK@gBzVgSIK?HRoQ?Zc=5i69bs!#=3
zlbXQv&6;o2(&bo%qF*zF`y((C@=CV9%>qct`!KufywJr;)m4>;p*TEP?sYI&Kv9Su
zJ^W|wKWENG4G|O@A3gYoP}?Iilrao7uQtReE=H^wf*#rUweO)hPa$(3O^=LvAAj-V
z0}c0GW}4^d)rS$fqJrmDf#QgvX>%Wch+ztY)Op!q?U6%&MgY`C4H2|Gpm%0A)iR-s
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
LNkvXXu0mjf6n6`+
diff --git a/app/src/blue/res/mipmap-xxxhdpi/ic_shortcut_compose.png b/app/src/blue/res/mipmap-xxxhdpi/ic_shortcut_compose.png
deleted file mode 100644
index a1d41bcef64c02b3da588e65e54e10f21abaf617..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 12931
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
Yg)p+MkoDjGTmsO3XrNJ}W*7PY0Cv{oWdHyG
diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
index 333eb09d1..485f6a8a6 100644
--- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt
@@ -52,8 +52,6 @@ import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.report.ReportActivity
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Account
-import com.keylesspalace.tusky.entity.Field
-import com.keylesspalace.tusky.entity.IdentityProof
import com.keylesspalace.tusky.entity.Relationship
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
import com.keylesspalace.tusky.interfaces.LinkListener
@@ -311,7 +309,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
* Subscribe to data loaded at the view model
*/
private fun subscribeObservables() {
- viewModel.accountData.observe(this, Observer> {
+ viewModel.accountData.observe(this, Observer {
when (it) {
is Success -> onAccountChanged(it.data)
is Error -> {
@@ -321,7 +319,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
}
}
})
- viewModel.relationshipData.observe(this, Observer> {
+ viewModel.relationshipData.observe(this, Observer {
val relation = it?.data
if (relation != null) {
onRelationshipChanged(relation)
@@ -334,7 +332,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
}
})
- viewModel.accountFieldData.observe(this, Observer>> {
+ viewModel.accountFieldData.observe(this, Observer {
accountFieldAdapter.fields = it
accountFieldAdapter.notifyDataSetChanged()
diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt
index 9477b8440..06edd80bb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/AccountsInListFragment.kt
@@ -77,7 +77,7 @@ class AccountsInListFragment : DialogFragment(), Injectable {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.TuskyDialogFragmentStyle)
viewModel = viewModelFactory.create(AccountsInListViewModel::class.java)
- val args = arguments!!
+ val args = requireArguments()
listId = args.getString(LIST_ID_ARG)!!
listName = args.getString(LIST_NAME_ARG)!!
@@ -255,12 +255,12 @@ class AccountsInListFragment : DialogFragment(), Injectable {
loadAvatar(account.avatar, avatar, radius, animateAvatar)
rejectButton.apply {
- if (inAList) {
+ contentDescription = if (inAList) {
setImageResource(R.drawable.ic_reject_24dp)
- contentDescription = getString(R.string.action_remove_from_list)
+ getString(R.string.action_remove_from_list)
} else {
setImageResource(R.drawable.ic_plus_24dp)
- contentDescription = getString(R.string.action_add_to_list)
+ getString(R.string.action_add_to_list)
}
}
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt
index 54aca8971..539dcfe01 100644
--- a/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt
@@ -135,7 +135,7 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
val positiveButton = dialog.getButton(Dialog.BUTTON_POSITIVE)
editText.onTextChanged { s, _, _, _ ->
- positiveButton.isEnabled = !s.isNullOrBlank()
+ positiveButton.isEnabled = !s.isBlank()
}
editText.setText(list?.title)
editText.text?.let { editText.setSelection(it.length) }
diff --git a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt
index 864a7208a..26e85deba 100644
--- a/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt
@@ -277,7 +277,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
addTabAdapter.updateData(addableTabs)
maxTabsInfo.visible(addableTabs.size == 0 || currentTabs.size >= MAX_TAB_COUNT)
- currentTabsAdapter.setRemoveButtonVisible(currentTabs.size > MIN_TAB_COUNT);
+ currentTabsAdapter.setRemoveButtonVisible(currentTabs.size > MIN_TAB_COUNT)
}
override fun onStartDelete(viewHolder: RecyclerView.ViewHolder) {
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt
index 0f89c3e00..74c97b6ca 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/AccountMediaFragment.kt
@@ -199,10 +199,10 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
val itemCount = layoutManager.itemCount
val lastItem = layoutManager.findLastCompletelyVisibleItemPosition()
if (itemCount <= lastItem + 3 && fetchingStatus == FetchingStatus.NOT_FETCHING) {
- statuses.lastOrNull()?.let { last ->
- Log.d(TAG, "Requesting statuses with max_id: ${last.id}, (bottom)")
+ statuses.lastOrNull()?.let { (id) ->
+ Log.d(TAG, "Requesting statuses with max_id: ${id}, (bottom)")
fetchingStatus = FetchingStatus.FETCHING_BOTTOM
- currentCall = api.accountStatuses(accountId, last.id, null, null, null, true, null)
+ currentCall = api.accountStatuses(accountId, id, null, null, null, true, null)
currentCall?.enqueue(bottomCallback)
}
}
@@ -254,7 +254,7 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
if (view != null && activity != null) {
val url = items[currentIndex].attachment.url
ViewCompat.setTransitionName(view, url)
- val options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity!!, view, url)
+ val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, url)
startActivity(intent, options.toBundle())
} else {
startActivity(intent)
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
index 0c20cfafe..7cbfde5ef 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java
@@ -212,7 +212,7 @@ public class TimelineFragment extends SFragment implements
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Bundle arguments = Objects.requireNonNull(getArguments());
+ Bundle arguments = requireArguments();
kind = Kind.valueOf(arguments.getString(KIND_ARG));
if (kind == Kind.TAG
|| kind == Kind.USER
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt
index 1c3672519..2831100db 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt
@@ -89,7 +89,7 @@ class ViewImageFragment : ViewMediaFragment() {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- toolbar = activity!!.toolbar
+ toolbar = requireActivity().toolbar
this.transition = BehaviorSubject.create()
return inflater.inflate(R.layout.fragment_view_image, container, false)
}
@@ -97,7 +97,7 @@ class ViewImageFragment : ViewMediaFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- val arguments = this.arguments!!
+ val arguments = this.requireArguments()
val attachment = arguments.getParcelable(ARG_ATTACHMENT)
this.shouldStartTransition = arguments.getBoolean(ARG_START_POSTPONED_TRANSITION)
val url: String?
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt
index 0b3a23a70..09c29d473 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt
@@ -136,12 +136,12 @@ class ViewVideoFragment : ViewMediaFragment() {
progressBar.hide()
mp.isLooping = true
- if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
+ if (requireArguments().getBoolean(ARG_START_POSTPONED_TRANSITION)) {
videoView.start()
}
}
- if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
+ if (requireArguments().getBoolean(ARG_START_POSTPONED_TRANSITION)) {
mediaActivity.onBringUp()
}
}
@@ -151,7 +151,7 @@ class ViewVideoFragment : ViewMediaFragment() {
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- toolbar = activity!!.toolbar
+ toolbar = requireActivity().toolbar
mediaActivity = activity as ViewMediaActivity
return inflater.inflate(R.layout.fragment_view_video, container, false)
}
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index daa6252ad..bbffc0a91 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -7,7 +7,6 @@
8dp
0dp
120dp
- 8dp
14dp
16dp
36dp
diff --git a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
index 8b0c916aa..12f7bab54 100644
--- a/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/FilterTest.kt
@@ -11,6 +11,7 @@ import com.keylesspalace.tusky.fragment.SFragment
import com.keylesspalace.tusky.network.MastodonApi
import com.nhaarman.mockitokotlin2.mock
import okhttp3.Request
+import okio.Timeout
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -99,6 +100,10 @@ class FilterTest {
)
)
}
+
+ override fun timeout(): Timeout {
+ throw Error("not implemented")
+ }
})
activity.mastodonApi = apiMock
diff --git a/app/src/test/java/com/keylesspalace/tusky/TuskyApplication.kt b/app/src/test/java/com/keylesspalace/tusky/TuskyApplication.kt
index aaa2b349e..7f82e2495 100644
--- a/app/src/test/java/com/keylesspalace/tusky/TuskyApplication.kt
+++ b/app/src/test/java/com/keylesspalace/tusky/TuskyApplication.kt
@@ -18,13 +18,9 @@ package com.keylesspalace.tusky
import android.app.Application
import android.content.Context
import android.content.res.Configuration
-import android.util.Log
import androidx.emoji.text.EmojiCompat
import com.keylesspalace.tusky.util.LocaleManager
-import dagger.android.DispatchingAndroidInjector
-import dagger.android.HasAndroidInjector
import de.c1710.filemojicompat.FileEmojiCompatConfig
-import javax.inject.Inject
// override TuskyApplication for Robolectric tests, only initialize the necessary stuff
class TuskyApplication : Application() {
diff --git a/build.gradle b/build.gradle
index 38f3d2b41..cb3ee21f3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,11 @@
buildscript {
- ext.kotlin_version = '1.3.70'
+ ext.kotlin_version = '1.3.71'
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.6.1'
+ classpath 'com.android.tools.build:gradle:3.6.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6254d2d4a..6623300be 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew.bat b/gradlew.bat
index 24467a141..9109989e3 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
From d44eada14066592977bd12b1ed8ad238f27fef58 Mon Sep 17 00:00:00 2001
From: Ivan Kupalov
Date: Mon, 6 Apr 2020 19:28:43 +0200
Subject: [PATCH 42/42] Fix reblog confirm (#1723)
* Fix reblog confirmation default value
* Fix triggering reblog before confirming it
* Fix accidents caused by reblog confirmation
---
.../tusky/adapter/StatusBaseViewHolder.java | 19 +++++++++++--------
.../conversation/ConversationsFragment.kt | 2 +-
.../fragments/ReportStatusesFragment.kt | 2 +-
.../fragments/SearchStatusesFragment.kt | 2 +-
.../tusky/fragment/TimelineFragment.java | 4 ----
5 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
index f808cb1bb..ae0dfdbb5 100644
--- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
+++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java
@@ -611,10 +611,10 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
sensitiveMediaShow.setVisibility(View.GONE);
}
- protected void setupButtons(final StatusActionListener listener, final String accountId,
+ protected void setupButtons(final StatusActionListener listener,
+ final String accountId,
final String statusContent,
StatusDisplayOptions statusDisplayOptions) {
-
avatar.setOnClickListener(v -> listener.onViewAccount(accountId));
replyButton.setOnClickListener(v -> {
int position = getAdapterPosition();
@@ -624,15 +624,18 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
});
if (reblogButton != null) {
reblogButton.setEventListener((button, buttonState) -> {
+ // return true to play animaion
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
- listener.onReblog(!buttonState, position);
- }
- if (statusDisplayOptions.confirmReblogs()) {
- showConfirmReblogDialog(listener, statusContent, buttonState, position);
- return false;
+ if (statusDisplayOptions.confirmReblogs()) {
+ showConfirmReblogDialog(listener, statusContent, buttonState, position);
+ return false;
+ } else {
+ listener.onReblog(!buttonState, position);
+ return true;
+ }
} else {
- return true;
+ return false;
}
});
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
index a91ed2f08..aa9559507 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
@@ -67,7 +67,7 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
useBlurhash = preferences.getBoolean("useBlurhash", true),
cardViewMode = CardViewMode.NONE,
- confirmReblogs = preferences.getBoolean("confirmReblogs", true)
+ confirmReblogs = preferences.getBoolean("confirmReblogs", false)
)
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
index dad6fe1f5..08fbf40fb 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
@@ -118,7 +118,7 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler {
showBotOverlay = false,
useBlurhash = preferences.getBoolean("useBlurhash", true),
cardViewMode = CardViewMode.NONE,
- confirmReblogs = preferences.getBoolean("confirmReblogs", true)
+ confirmReblogs = preferences.getBoolean("confirmReblogs", false)
)
adapter = StatusesAdapter(statusDisplayOptions,
diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
index 4e5f64fbe..8ff67053b 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt
@@ -83,7 +83,7 @@ class SearchStatusesFragment : SearchFragment