From bf5e0a4b81b7a5eae88852a801eb55a29cd7ea97 Mon Sep 17 00:00:00 2001 From: kyori19 Date: Tue, 28 Jan 2020 01:11:10 +0900 Subject: [PATCH] Show announcements --- .../com/keylesspalace/tusky/MainActivity.java | 2 +- .../tusky/ModalTimelineActivity.kt | 2 +- .../keylesspalace/tusky/StatusListActivity.kt | 7 +- .../keylesspalace/tusky/ViewTagActivity.java | 2 +- .../tusky/entity/Announcement.kt | 27 +++++ .../tusky/network/MastodonApi.kt | 3 + .../net/accelf/yuito/QuickTootHelper.java | 110 +++++++++++++++++- .../drawable-anydpi/ic_arrow_drop_down.xml | 9 ++ .../res/drawable-anydpi/ic_arrow_drop_up.xml | 9 ++ .../res/drawable-anydpi/ic_chevron_left.xml | 9 ++ .../res/drawable-anydpi/ic_chevron_right.xml | 9 ++ app/src/main/res/layout/view_quick_toot.xml | 63 ++++++++++ 12 files changed, 243 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/entity/Announcement.kt create mode 100644 app/src/main/res/drawable-anydpi/ic_arrow_drop_down.xml create mode 100644 app/src/main/res/drawable-anydpi/ic_arrow_drop_up.xml create mode 100644 app/src/main/res/drawable-anydpi/ic_chevron_left.xml create mode 100644 app/src/main/res/drawable-anydpi/ic_chevron_right.xml diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index bff57dccb..80a9d4aa9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -209,7 +209,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut viewPager = findViewById(R.id.pager); ConstraintLayout quickTootContainer = findViewById(R.id.quick_toot_container); - QuickTootHelper quickTootHelper = new QuickTootHelper(quickTootContainer, accountManager, eventHub); + QuickTootHelper quickTootHelper = new QuickTootHelper(this, quickTootContainer, accountManager, eventHub); composeButton.setOnClickListener(v -> quickTootHelper.composeButton()); tabLayout.requestFocus(); diff --git a/app/src/main/java/com/keylesspalace/tusky/ModalTimelineActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ModalTimelineActivity.kt index 8077d628d..fc42080a6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ModalTimelineActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ModalTimelineActivity.kt @@ -64,7 +64,7 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn val quickTootContainer = findViewById(R.id.quick_toot_container) val composeButton = findViewById(R.id.floating_btn) - val quickTootHelper = QuickTootHelper(quickTootContainer, accountManager, eventHub) + val quickTootHelper = QuickTootHelper(this, quickTootContainer, accountManager, eventHub) eventHub.events .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index aea649a25..dfc9da07a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -24,14 +24,10 @@ import androidx.fragment.app.commit import androidx.lifecycle.Lifecycle import com.google.android.material.floatingactionbutton.FloatingActionButton import com.keylesspalace.tusky.appstore.EventHub - import com.keylesspalace.tusky.fragment.TimelineFragment import com.keylesspalace.tusky.fragment.TimelineFragment.Kind import com.uber.autodispose.AutoDispose import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider - -import javax.inject.Inject - import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector import io.reactivex.android.schedulers.AndroidSchedulers @@ -39,6 +35,7 @@ import kotlinx.android.extensions.CacheImplementation import kotlinx.android.extensions.ContainerOptions import kotlinx.android.synthetic.main.toolbar_basic.* import net.accelf.yuito.QuickTootHelper +import javax.inject.Inject class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { @@ -76,7 +73,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { val quickTootContainer = findViewById(R.id.quick_toot_container) val composeButton = findViewById(R.id.floating_btn) - val quickTootHelper = QuickTootHelper(quickTootContainer, accountManager, eventHub) + val quickTootHelper = QuickTootHelper(this, quickTootContainer, accountManager, eventHub) eventHub.events .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java b/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java index a23c52bf5..0f8438569 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ViewTagActivity.java @@ -81,7 +81,7 @@ public class ViewTagActivity extends BottomSheetActivity implements HasAndroidIn fragmentTransaction.commit(); ConstraintLayout quickTootContainer = findViewById(R.id.quick_toot_container); - QuickTootHelper quickTootHelper = new QuickTootHelper(quickTootContainer, accountManager, eventHub); + QuickTootHelper quickTootHelper = new QuickTootHelper(this, quickTootContainer, accountManager, eventHub); eventHub.getEvents() .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Announcement.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Announcement.kt new file mode 100644 index 000000000..2b3a2ac18 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Announcement.kt @@ -0,0 +1,27 @@ +package com.keylesspalace.tusky.entity + +import android.text.Spanned +import com.google.gson.annotations.SerializedName +import java.util.* + +data class Announcement( + val id: String, + val content: Spanned, + @SerializedName("starts_at") val startsAt: Date, + @SerializedName("ends_at") val endsAt: Date, + @SerializedName("all_day") val allDay: Boolean, + val emojis: List, + val mentions: Array +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + + val announcement = other as Announcement? + return id == announcement?.id + } + + override fun hashCode(): Int { + return id.hashCode() + } +} 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 da61cb6a0..2886af3cc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -479,6 +479,9 @@ interface MastodonApi { @Field("choices[]") choices: List ): Single + @GET("api/v1/announcements") + fun listAnnouncements(): Single> + @POST("api/v1/accounts/{id}/block") fun blockAccountObservable( @Path("id") accountId: String diff --git a/app/src/main/java/net/accelf/yuito/QuickTootHelper.java b/app/src/main/java/net/accelf/yuito/QuickTootHelper.java index 976d4fae2..82385ffa6 100644 --- a/app/src/main/java/net/accelf/yuito/QuickTootHelper.java +++ b/app/src/main/java/net/accelf/yuito/QuickTootHelper.java @@ -5,14 +5,21 @@ import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; import android.preference.PreferenceManager; +import android.view.View; import android.widget.Button; import android.widget.EditText; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.lifecycle.Lifecycle; +import com.keylesspalace.tusky.AccountActivity; +import com.keylesspalace.tusky.BottomSheetActivity; +import com.keylesspalace.tusky.PostLookupFallbackBehavior; import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.ViewTagActivity; import com.keylesspalace.tusky.appstore.DrawerFooterClickedEvent; import com.keylesspalace.tusky.appstore.Event; import com.keylesspalace.tusky.appstore.EventHub; @@ -21,16 +28,26 @@ import com.keylesspalace.tusky.appstore.QuickReplyEvent; import com.keylesspalace.tusky.components.compose.ComposeActivity; import com.keylesspalace.tusky.db.AccountEntity; import com.keylesspalace.tusky.db.AccountManager; +import com.keylesspalace.tusky.entity.Announcement; import com.keylesspalace.tusky.entity.Status; +import com.keylesspalace.tusky.interfaces.LinkListener; +import com.keylesspalace.tusky.util.LinkHelper; +import com.keylesspalace.tusky.util.ListUtils; import com.keylesspalace.tusky.util.ThemeUtils; import java.util.Arrays; import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; import java.util.Set; +import io.reactivex.android.schedulers.AndroidSchedulers; + import static com.keylesspalace.tusky.components.compose.ComposeActivity.CAN_USE_UNLEAKABLE; import static com.keylesspalace.tusky.components.compose.ComposeActivity.PREF_DEFAULT_TAG; import static com.keylesspalace.tusky.components.compose.ComposeActivity.PREF_USE_DEFAULT_TAG; +import static com.uber.autodispose.AutoDispose.autoDisposable; +import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from; public class QuickTootHelper { @@ -39,22 +56,36 @@ public class QuickTootHelper { private TextView defaultTagInfo; private ImageView visibilityButton; private EditText tootEditText; + private ImageButton openAnnouncementsButton; + private TextView announcementsText; + private ImageButton prevButton; + private ImageButton nextButton; + private TextView announcementsCountText; private SharedPreferences defPrefs; private String domain; private String loggedInUsername; private EventHub eventHub; + private LinkListener listener; private Status inReplyTo; + private boolean open = false; + private int index = 0; + private List announcements; private static final String PREF_CURRENT_VISIBILITY = "current_visibility"; - public QuickTootHelper(ConstraintLayout root, AccountManager accountManager, EventHub eventHub) { + public QuickTootHelper(BottomSheetActivity activity, ConstraintLayout root, AccountManager accountManager, EventHub eventHub) { context = root.getContext(); quickReplyInfo = root.findViewById(R.id.quick_reply_info); defaultTagInfo = root.findViewById(R.id.default_tag_info); visibilityButton = root.findViewById(R.id.visibility_button); tootEditText = root.findViewById(R.id.toot_edit_text); + openAnnouncementsButton = root.findViewById(R.id.button_open_announcements); + announcementsText = root.findViewById(R.id.text_view_announcements); + prevButton = root.findViewById(R.id.button_prev_announcements); + nextButton = root.findViewById(R.id.button_next_announcements); + announcementsCountText = root.findViewById(R.id.text_view_announcements_count); Button quickTootButton = root.findViewById(R.id.toot_button); context = root.getContext(); @@ -71,6 +102,37 @@ public class QuickTootHelper { updateDefaultTagInfo(); visibilityButton.setOnClickListener(v -> setNextVisibility()); quickTootButton.setOnClickListener(v -> quickToot()); + + listener = new LinkListener() { + @Override + public void onViewTag(String tag) { + context.startActivity(ViewTagActivity.getIntent(context, tag)); + } + + @Override + public void onViewAccount(String id) { + context.startActivity(AccountActivity.getIntent(context, id)); + } + + @Override + public void onViewUrl(String url, String text) { + activity.viewUrl(url, text, PostLookupFallbackBehavior.OPEN_IN_BROWSER); + } + }; + activity.mastodonApi.listAnnouncements() + .observeOn(AndroidSchedulers.mainThread()) + .as(autoDisposable(from(activity, Lifecycle.Event.ON_DESTROY))) + .subscribe( + a -> { + announcements = a; + updateAnnouncements(); + }, + Throwable::printStackTrace + ); + updateAnnouncements(); + openAnnouncementsButton.setOnClickListener(v -> toggleOpenAnnouncements()); + prevButton.setOnClickListener(v -> prevAnnouncement()); + nextButton.setOnClickListener(v -> nextAnnouncement()); } public void composeButton() { @@ -236,4 +298,50 @@ public class QuickTootHelper { eventHub.dispatch(new PreferenceChangedEvent(PREF_CURRENT_VISIBILITY)); updateVisibilityButton(); } + + private void updateAnnouncements() { + if (ListUtils.isEmpty(announcements)) { + openAnnouncementsButton.setVisibility(View.GONE); + announcementsText.setVisibility(View.GONE); + announcementsCountText.setVisibility(View.GONE); + prevButton.setVisibility(View.GONE); + nextButton.setVisibility(View.GONE); + } else { + openAnnouncementsButton.setVisibility(View.VISIBLE); + announcementsText.setVisibility(View.VISIBLE); + announcementsCountText.setVisibility(View.VISIBLE); + if (open) { + prevButton.setVisibility(View.VISIBLE); + nextButton.setVisibility(View.VISIBLE); + } else { + prevButton.setVisibility(View.GONE); + nextButton.setVisibility(View.GONE); + } + + openAnnouncementsButton.setImageDrawable(context.getDrawable(open ? R.drawable.ic_arrow_drop_down : R.drawable.ic_arrow_drop_up)); + announcementsText.setSingleLine(!open); + announcementsCountText.setText(String.format(Locale.getDefault(), "(%d/%d)", index + 1, announcements.size())); + Announcement announcement = announcements.get(index); + LinkHelper.setClickableText(announcementsText, announcement.getContent(), announcement.getMentions(), listener, false); + } + } + + private void toggleOpenAnnouncements() { + open = !open; + updateAnnouncements(); + } + + private void prevAnnouncement() { + if (index > 0) { + index--; + updateAnnouncements(); + } + } + + private void nextAnnouncement() { + if (index < announcements.size() - 1) { + index++; + updateAnnouncements(); + } + } } diff --git a/app/src/main/res/drawable-anydpi/ic_arrow_drop_down.xml b/app/src/main/res/drawable-anydpi/ic_arrow_drop_down.xml new file mode 100644 index 000000000..62b27ef0b --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_arrow_drop_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_arrow_drop_up.xml b/app/src/main/res/drawable-anydpi/ic_arrow_drop_up.xml new file mode 100644 index 000000000..b1442ce15 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_arrow_drop_up.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_chevron_left.xml b/app/src/main/res/drawable-anydpi/ic_chevron_left.xml new file mode 100644 index 000000000..e6bb3ca92 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_chevron_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_chevron_right.xml b/app/src/main/res/drawable-anydpi/ic_chevron_right.xml new file mode 100644 index 000000000..24835127d --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_chevron_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/view_quick_toot.xml b/app/src/main/res/layout/view_quick_toot.xml index 1c0e6742c..41f979424 100644 --- a/app/src/main/res/layout/view_quick_toot.xml +++ b/app/src/main/res/layout/view_quick_toot.xml @@ -5,6 +5,69 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + +