Refactor quick compose

* Stash visibility while composing reply
* Remove announcements
This commit is contained in:
kyori19 2020-09-07 22:57:59 +09:00
parent bb5106a18d
commit 025ba68df4
23 changed files with 326 additions and 563 deletions

View File

@ -34,10 +34,10 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.ImageView import android.widget.ImageView
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
@ -65,6 +65,7 @@ import com.keylesspalace.tusky.components.preference.PreferencesActivity
import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity import com.keylesspalace.tusky.components.scheduled.ScheduledTootActivity
import com.keylesspalace.tusky.components.search.SearchActivity import com.keylesspalace.tusky.components.search.SearchActivity
import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountEntity
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.fragment.NotificationsFragment import com.keylesspalace.tusky.fragment.NotificationsFragment
import com.keylesspalace.tusky.fragment.SFragment import com.keylesspalace.tusky.fragment.SFragment
@ -94,7 +95,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import net.accelf.yuito.FooterDrawerItem import net.accelf.yuito.FooterDrawerItem
import net.accelf.yuito.QuickTootHelper import net.accelf.yuito.QuickTootViewModel
import javax.inject.Inject import javax.inject.Inject
class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector { class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
@ -110,6 +111,11 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
@Inject @Inject
lateinit var conversationRepository: ConversationsRepository lateinit var conversationRepository: ConversationsRepository
@Inject
lateinit var viewModelFactory: ViewModelFactory
private val quickTootViewModel: QuickTootViewModel by viewModels { viewModelFactory }
private lateinit var header: AccountHeaderView private lateinit var header: AccountHeaderView
private var streamingTabsCount = 0 private var streamingTabsCount = 0
@ -182,10 +188,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
val quickTootHelper = QuickTootHelper(this, quickTootContainer as ConstraintLayout?, accountManager, eventHub) viewQuickToot.attachViewModel(quickTootViewModel, this)
composeButton.setOnClickListener { composeButton.setOnClickListener(viewQuickToot::onFABClicked)
quickTootHelper.composeButton()
}
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false) val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
mainToolbar.visible(!hideTopToolbar) mainToolbar.visible(!hideTopToolbar)
@ -238,7 +242,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
is ProfileEditedEvent -> onFetchUserInfoSuccess(event.newProfileData) is ProfileEditedEvent -> onFetchUserInfoSuccess(event.newProfileData)
is MainTabsChangedEvent -> setupTabs(false) is MainTabsChangedEvent -> setupTabs(false)
} }
quickTootHelper.handleEvent(event) viewQuickToot.handleEvent(event)
} }
// Flush old media that was cached for sharing // Flush old media that was cached for sharing
@ -475,7 +479,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
) )
addStickyDrawerItems( addStickyDrawerItems(
FooterDrawerItem().apply { FooterDrawerItem().apply {
eventHub = this@MainActivity.eventHub
setSubscribeProxy( setSubscribeProxy(
mastodonApi.getInstance() mastodonApi.getInstance()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@ -4,10 +4,11 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.constraintlayout.widget.ConstraintLayout import androidx.activity.viewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.fragment.TimelineFragment import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.ActionButtonActivity
import com.uber.autodispose.AutoDispose.autoDisposable import com.uber.autodispose.AutoDispose.autoDisposable
@ -15,8 +16,10 @@ import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.activity_main.viewQuickToot
import kotlinx.android.synthetic.main.activity_modal_timeline.*
import kotlinx.android.synthetic.main.toolbar_basic.* import kotlinx.android.synthetic.main.toolbar_basic.*
import net.accelf.yuito.QuickTootHelper import net.accelf.yuito.QuickTootViewModel
import javax.inject.Inject import javax.inject.Inject
class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector { class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
@ -40,6 +43,10 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
@Inject @Inject
lateinit var eventHub: EventHub lateinit var eventHub: EventHub
@Inject
lateinit var viewModelFactory: ViewModelFactory
private val quickTootViewModel: QuickTootViewModel by viewModels { viewModelFactory }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -62,15 +69,13 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn
.commit() .commit()
} }
val quickTootContainer = findViewById<ConstraintLayout>(R.id.quick_toot_container) viewQuickToot.attachViewModel(quickTootViewModel, this)
val composeButton = findViewById<FloatingActionButton>(R.id.floating_btn)
val quickTootHelper = QuickTootHelper(this, quickTootContainer, accountManager, eventHub)
eventHub.events eventHub.events
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.`as`(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .`as`(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(quickTootHelper::handleEvent) .subscribe(viewQuickToot::handleEvent)
composeButton.setOnClickListener { quickTootHelper.composeButton() } floating_btn.setOnClickListener(viewQuickToot::onFABClicked)
} }
override fun getActionButton(): FloatingActionButton? = null override fun getActionButton(): FloatingActionButton? = null

View File

@ -19,11 +19,11 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.constraintlayout.widget.ConstraintLayout import androidx.activity.viewModels
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.fragment.TimelineFragment import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.fragment.TimelineFragment.Kind import com.keylesspalace.tusky.fragment.TimelineFragment.Kind
import com.uber.autodispose.AutoDispose import com.uber.autodispose.AutoDispose
@ -33,8 +33,9 @@ import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.extensions.CacheImplementation import kotlinx.android.extensions.CacheImplementation
import kotlinx.android.extensions.ContainerOptions import kotlinx.android.extensions.ContainerOptions
import kotlinx.android.synthetic.main.activity_statuslist.*
import kotlinx.android.synthetic.main.toolbar_basic.* import kotlinx.android.synthetic.main.toolbar_basic.*
import net.accelf.yuito.QuickTootHelper import net.accelf.yuito.QuickTootViewModel
import javax.inject.Inject import javax.inject.Inject
class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
@ -43,6 +44,10 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
@Inject @Inject
lateinit var eventHub: EventHub lateinit var eventHub: EventHub
@Inject
lateinit var viewModelFactory: ViewModelFactory
private val quickTootViewModel: QuickTootViewModel by viewModels{ viewModelFactory }
private val kind: Kind private val kind: Kind
get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!) get() = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!)
@ -71,16 +76,13 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
replace(R.id.fragment_container, fragment) replace(R.id.fragment_container, fragment)
} }
val quickTootContainer = findViewById<ConstraintLayout>(R.id.quick_toot_container) viewQuickToot.attachViewModel(quickTootViewModel, this)
val composeButton = findViewById<FloatingActionButton>(R.id.floating_btn)
val quickTootHelper = QuickTootHelper(this, quickTootContainer, accountManager, eventHub)
eventHub.events eventHub.events
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))) .`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(quickTootHelper::handleEvent) .subscribe(viewQuickToot::handleEvent)
composeButton.setOnClickListener { quickTootHelper.composeButton() } floating_btn.setOnClickListener(viewQuickToot::onFABClicked)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@ -23,15 +23,16 @@ import android.view.MenuItem;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import com.keylesspalace.tusky.appstore.EventHub; import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.di.ViewModelFactory;
import com.keylesspalace.tusky.fragment.TimelineFragment; import com.keylesspalace.tusky.fragment.TimelineFragment;
import net.accelf.yuito.QuickTootHelper; import net.accelf.yuito.QuickTootView;
import net.accelf.yuito.QuickTootViewModel;
import java.util.Collections; import java.util.Collections;
@ -53,6 +54,8 @@ public class ViewTagActivity extends BottomSheetActivity implements HasAndroidIn
public DispatchingAndroidInjector<Object> dispatchingAndroidInjector; public DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
@Inject @Inject
public EventHub eventHub; public EventHub eventHub;
@Inject
public ViewModelFactory viewModelFactory;
public static Intent getIntent(Context context, String tag){ public static Intent getIntent(Context context, String tag){
Intent intent = new Intent(context,ViewTagActivity.class); Intent intent = new Intent(context,ViewTagActivity.class);
@ -82,13 +85,14 @@ public class ViewTagActivity extends BottomSheetActivity implements HasAndroidIn
fragmentTransaction.replace(R.id.fragment_container, fragment); fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit(); fragmentTransaction.commit();
ConstraintLayout quickTootContainer = findViewById(R.id.quick_toot_container); QuickTootViewModel quickTootViewModel = viewModelFactory.create(QuickTootViewModel.class);
QuickTootHelper quickTootHelper = new QuickTootHelper(this, quickTootContainer, accountManager, eventHub); QuickTootView quickTootView = findViewById(R.id.viewQuickToot);
quickTootView.attachViewModel(quickTootViewModel, this);
eventHub.getEvents() eventHub.getEvents()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(quickTootHelper::handleEvent); .subscribe(quickTootView::handleEvent);
} }
@Override @Override

View File

@ -23,4 +23,3 @@ data class PollVoteEvent(val statusId: String, val poll: Poll) : Dispatchable
data class DomainMuteEvent(val instance: String): Dispatchable data class DomainMuteEvent(val instance: String): Dispatchable
data class QuickReplyEvent(val status: Status) : Dispatchable data class QuickReplyEvent(val status: Status) : Dispatchable
data class StreamUpdateEvent(val status: Status, val targetKind: TimelineFragment.Kind, val targetIdentifier: String?, val first: Boolean) : Dispatchable data class StreamUpdateEvent(val status: Status, val targetKind: TimelineFragment.Kind, val targetIdentifier: String?, val first: Boolean) : Dispatchable
data class DrawerFooterClickedEvent(val placeholder: Boolean) : Dispatchable

View File

@ -17,6 +17,7 @@ import dagger.Binds
import dagger.MapKey import dagger.MapKey
import dagger.Module import dagger.Module
import dagger.multibindings.IntoMap import dagger.multibindings.IntoMap
import net.accelf.yuito.QuickTootViewModel
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
import javax.inject.Singleton import javax.inject.Singleton
@ -85,5 +86,10 @@ abstract class ViewModelModule {
@ViewModelKey(ScheduledTootViewModel::class) @ViewModelKey(ScheduledTootViewModel::class)
internal abstract fun scheduledTootViewModel(viewModel: ScheduledTootViewModel): ViewModel internal abstract fun scheduledTootViewModel(viewModel: ScheduledTootViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(QuickTootViewModel::class)
internal abstract fun quickTootViewModel(viewModel: QuickTootViewModel): ViewModel
//Add more ViewModels here //Add more ViewModels here
} }

View File

@ -1,27 +0,0 @@
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<Emoji>,
val mentions: Array<Status.Mention>
) {
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()
}
}

View File

@ -505,9 +505,6 @@ interface MastodonApi {
@Field("choices[]") choices: List<Int> @Field("choices[]") choices: List<Int>
): Single<Poll> ): Single<Poll>
@GET("api/v1/announcements")
fun listAnnouncements(): Single<List<Announcement>>
@POST("api/v1/accounts/{id}/block") @POST("api/v1/accounts/{id}/block")
fun blockAccountObservable( fun blockAccountObservable(
@Path("id") accountId: String @Path("id") accountId: String

View File

@ -5,20 +5,15 @@ import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.DrawerFooterClickedEvent
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.entity.Instance import com.keylesspalace.tusky.entity.Instance
import com.mikepenz.materialdrawer.model.AbstractDrawerItem import com.mikepenz.materialdrawer.model.AbstractDrawerItem
import com.uber.autodispose.SingleSubscribeProxy import com.uber.autodispose.SingleSubscribeProxy
import kotlinx.android.synthetic.main.item_drawer_footer.view.*
class FooterDrawerItem : AbstractDrawerItem<FooterDrawerItem, FooterDrawerItem.ViewHolder>() { class FooterDrawerItem : AbstractDrawerItem<FooterDrawerItem, FooterDrawerItem.ViewHolder>() {
override val type: Int override val type = R.id.instanceData
get() = R.id.instanceData
override val layoutRes: Int override val layoutRes = R.layout.item_drawer_footer
get() = R.layout.item_drawer_footer
lateinit var eventHub: EventHub
private lateinit var context: Context private lateinit var context: Context
private lateinit var instanceData: TextView private lateinit var instanceData: TextView
@ -29,24 +24,9 @@ class FooterDrawerItem : AbstractDrawerItem<FooterDrawerItem, FooterDrawerItem.V
instanceData = holder.instanceData instanceData = holder.instanceData
holder.itemView.setPadding(0, 0, 0, 0) holder.itemView.setPadding(0, 0, 0, 0)
instanceData.setTextColor(instanceData.hintTextColors) instanceData.setTextColor(instanceData.hintTextColors)
onDrawerItemClickListener = { _, _, _ ->
var result = true
var text = instanceData.text.toString()
text += "?"
if (text.endsWith("???????")) {
text = text.substring(0, text.length - 7)
eventHub.dispatch(DrawerFooterClickedEvent(true))
isExpanded = false
result = false
}
instanceData.text = text
result
}
} }
override fun getViewHolder(v: View): ViewHolder { override fun getViewHolder(v: View) = ViewHolder(v)
return ViewHolder(v)
}
fun setSubscribeProxy(subscribeProxy: SingleSubscribeProxy<Instance>) { fun setSubscribeProxy(subscribeProxy: SingleSubscribeProxy<Instance>) {
subscribeProxy.subscribe( subscribeProxy.subscribe(
@ -60,6 +40,6 @@ class FooterDrawerItem : AbstractDrawerItem<FooterDrawerItem, FooterDrawerItem.V
} }
class ViewHolder internal constructor(internal val view: View): RecyclerView.ViewHolder(view) { class ViewHolder internal constructor(internal val view: View): RecyclerView.ViewHolder(view) {
internal val instanceData: TextView = view.findViewById(R.id.instanceData) internal val instanceData = view.instanceData
} }
} }

View File

@ -1,346 +0,0 @@
package net.accelf.yuito;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
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;
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
import com.keylesspalace.tusky.appstore.QuickReplyEvent;
import com.keylesspalace.tusky.components.compose.ComposeActivity;
import com.keylesspalace.tusky.components.compose.view.TootButton;
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 {
private Context context;
private TextView quickReplyInfo;
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 TootButton quickTootButton;
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<Announcement> announcements;
private static final String PREF_CURRENT_VISIBILITY = "current_visibility";
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);
quickTootButton = root.findViewById(R.id.toot_button);
context = root.getContext();
this.defPrefs = PreferenceManager.getDefaultSharedPreferences(context);
AccountEntity account = accountManager.getActiveAccount();
if (account != null) {
domain = account.getDomain();
loggedInUsername = account.getUsername();
}
this.eventHub = eventHub;
updateVisibilityButton();
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, PostLookupFallbackBehavior.OPEN_IN_BROWSER, text);
}
};
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());
announcementsText.setOnClickListener(v -> toggleOpenAnnouncements());
prevButton.setOnClickListener(v -> prevAnnouncement());
nextButton.setOnClickListener(v -> nextAnnouncement());
}
public void composeButton() {
if (tootEditText.getText().length() == 0 && inReplyTo == null) {
context.startActivity(getComposeIntent(context, true, false));
} else {
startComposeWithQuickComposeData();
}
}
public void handleEvent(Event event) {
if (event instanceof QuickReplyEvent) {
reply(((QuickReplyEvent) event).getStatus());
} else if (event instanceof PreferenceChangedEvent) {
switch (((PreferenceChangedEvent) event).getPreferenceKey()) {
case PREF_CURRENT_VISIBILITY: {
updateVisibilityButton();
break;
}
case PREF_DEFAULT_TAG:
case PREF_USE_DEFAULT_TAG: {
updateDefaultTagInfo();
break;
}
}
} else if (event instanceof DrawerFooterClickedEvent) {
tootEditText.setText("にゃーん");
}
}
private void reply(Status status) {
inReplyTo = status;
updateQuickReplyInfo();
}
private void startComposeWithQuickComposeData() {
Intent intent = getComposeIntent(context, false, false);
resetQuickCompose();
context.startActivity(intent);
}
private void quickToot() {
if (tootEditText.getText().toString().length() > 0) {
Intent intent = getComposeIntent(context, false, true);
resetQuickCompose();
context.startActivity(intent);
}
}
private Intent getComposeIntent(Context context, boolean onlyVisibility, boolean tootRightNow) {
ComposeActivity.ComposeOptions options = new ComposeActivity.ComposeOptions();
options.setVisibility(getCurrentVisibility());
if (onlyVisibility) {
return ComposeActivity.startIntent(context, options);
}
options.setTootText(tootEditText.getText().toString());
options.setTootRightNow(tootRightNow);
if (inReplyTo != null) {
Status.Mention[] mentions = inReplyTo.getMentions();
Set<String> mentionedUsernames = new LinkedHashSet<>();
mentionedUsernames.add(inReplyTo.getAccount().getUsername());
for (Status.Mention mention : mentions) {
mentionedUsernames.add(mention.getUsername());
}
mentionedUsernames.remove(loggedInUsername);
options.setInReplyToId(inReplyTo.getId());
options.setContentWarning(inReplyTo.getSpoilerText());
options.setMentionedUsernames(mentionedUsernames);
options.setReplyingStatusAuthor(inReplyTo.getAccount().getLocalUsername());
options.setReplyingStatusContent(inReplyTo.getContent().toString());
}
return ComposeActivity.startIntent(context, options);
}
private void resetQuickCompose() {
tootEditText.getText().clear();
inReplyTo = null;
updateQuickReplyInfo();
}
private void updateQuickReplyInfo() {
if (inReplyTo != null) {
quickReplyInfo.setText(String.format("Reply to : %s", inReplyTo.getAccount().getUsername()));
} else {
quickReplyInfo.setText("");
}
}
private void updateDefaultTagInfo() {
boolean useDefaultTag = defPrefs.getBoolean(PREF_USE_DEFAULT_TAG, false);
String defaultText = defPrefs.getString(PREF_DEFAULT_TAG, "");
if (useDefaultTag) {
defaultTagInfo.setText(String.format("%s : %s", context.getString(R.string.hint_default_text), defaultText));
defaultTagInfo.setTextColor(ThemeUtils.getColor(context, R.attr.colorInfo));
} else {
defaultTagInfo.setText(String.format("%s inactive", context.getString(R.string.hint_default_text)));
defaultTagInfo.setTextColor(ThemeUtils.getColor(context, android.R.attr.textColorTertiary));
}
}
private Status.Visibility getCurrentVisibility() {
Status.Visibility visibility = Status.Visibility.byNum(defPrefs.getInt(PREF_CURRENT_VISIBILITY, Status.Visibility.PUBLIC.getNum()));
if (!Arrays.asList(CAN_USE_UNLEAKABLE)
.contains(domain) && visibility == Status.Visibility.UNLEAKABLE) {
defPrefs.edit()
.putInt(PREF_CURRENT_VISIBILITY, Status.Visibility.PUBLIC.getNum())
.apply();
eventHub.dispatch(new PreferenceChangedEvent(PREF_CURRENT_VISIBILITY));
return Status.Visibility.PUBLIC;
}
return visibility;
}
private void updateVisibilityButton() {
Status.Visibility visibility = getCurrentVisibility();
quickTootButton.setStatusVisibility(visibility);
switch (visibility) {
case PUBLIC:
visibilityButton.setImageResource(R.drawable.ic_public_24dp);
break;
case UNLISTED:
visibilityButton.setImageResource(R.drawable.ic_lock_open_24dp);
break;
case PRIVATE:
visibilityButton.setImageResource(R.drawable.ic_lock_outline_24dp);
break;
case UNLEAKABLE:
visibilityButton.setImageResource(R.drawable.ic_low_vision_24dp);
break;
}
}
private void setNextVisibility() {
Status.Visibility visibility = getCurrentVisibility();
switch (visibility) {
case PUBLIC:
visibility = Status.Visibility.UNLISTED;
break;
case UNLISTED:
visibility = Status.Visibility.PRIVATE;
break;
case PRIVATE:
if (Arrays.asList(CAN_USE_UNLEAKABLE).contains(domain)) {
visibility = Status.Visibility.UNLEAKABLE;
} else {
visibility = Status.Visibility.PUBLIC;
}
break;
case UNLEAKABLE:
case UNKNOWN:
visibility = Status.Visibility.PUBLIC;
break;
}
defPrefs.edit()
.putInt(PREF_CURRENT_VISIBILITY, visibility.getNum())
.apply();
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(ContextCompat.getDrawable(context, 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();
}
}
}

View File

@ -0,0 +1,98 @@
package net.accelf.yuito
import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.observe
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.Event
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.appstore.QuickReplyEvent
import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.PREF_DEFAULT_TAG
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.PREF_USE_DEFAULT_TAG
import com.keylesspalace.tusky.util.ThemeUtils
import kotlinx.android.synthetic.main.view_quick_toot.view.*
class QuickTootView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
ConstraintLayout(context, attrs) {
private val preference by lazy { PreferenceManager.getDefaultSharedPreferences(context) }
private lateinit var viewModel: QuickTootViewModel
init {
LayoutInflater.from(context).inflate(R.layout.view_quick_toot, this, true)
}
fun attachViewModel(viewModel: QuickTootViewModel, owner: LifecycleOwner) {
this.viewModel = viewModel
buttonVisibility.attachViewModel(viewModel, owner)
viewModel.content.observe(owner) {
if (editTextContent.text.toString() != it) {
editTextContent.setText(it)
}
}
editTextContent.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
viewModel.content.value = s.toString()
}
})
viewModel.inReplyTo.observe(owner) {
textQuickReply.text = it?.let { "Reply to ${it.account.username}" } ?: ""
}
viewModel.defaultTag.observe(owner) {
textDefaultTag.text = it?.let { "${context.getString(R.string.hint_default_text)} : $it" }
?: "${context.getString(R.string.hint_default_text)} inactive"
textDefaultTag.setTextColor(ThemeUtils.getColor(context, it?.let { R.attr.colorInfo }
?: android.R.attr.textColorTertiary))
}
syncDefaultTag()
viewModel.visibility.observe(owner) {
buttonToot.setStatusVisibility(it)
}
buttonToot.setOnClickListener {
val intent = ComposeActivity.startIntent(it.context, viewModel.composeOptions(true))
viewModel.reset()
it.context.startActivity(intent)
}
}
private fun syncDefaultTag() {
viewModel.defaultTag.value = if (preference.getBoolean(PREF_USE_DEFAULT_TAG, false)) {
preference.getString(PREF_DEFAULT_TAG, null)
} else {
null
}
}
fun onFABClicked(view: View) {
val intent = ComposeActivity.startIntent(view.context, viewModel.composeOptions(false))
viewModel.reset()
view.context.startActivity(intent)
}
fun handleEvent(event: Event?) {
when (event) {
is QuickReplyEvent -> viewModel.reply(event)
is PreferenceChangedEvent -> {
if (event.preferenceKey in arrayOf(PREF_DEFAULT_TAG, PREF_USE_DEFAULT_TAG)) {
syncDefaultTag()
}
}
}
}
}

View File

@ -0,0 +1,91 @@
package net.accelf.yuito
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.keylesspalace.tusky.appstore.QuickReplyEvent
import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.CAN_USE_UNLEAKABLE
import com.keylesspalace.tusky.components.compose.mutableLiveData
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.entity.Status.Visibility
import javax.inject.Inject
class QuickTootViewModel @Inject constructor(
accountManager: AccountManager
): ViewModel() {
private val account = accountManager.activeAccount!!
private val unleakableAllowed by lazy { CAN_USE_UNLEAKABLE.contains(account.domain) }
val content = mutableLiveData("")
private val visibilityMutable = mutableLiveData(Visibility.PUBLIC)
val visibility: LiveData<Visibility> = visibilityMutable
private var stashedVisibility: Visibility? = null
private val inReplyToMutable: MutableLiveData<Status?> = mutableLiveData(null)
val inReplyTo: LiveData<Status?> = inReplyToMutable
val defaultTag: MutableLiveData<String?> = mutableLiveData(null)
fun setInitialVisibility(num: Int) {
visibilityMutable.value = (Visibility.byNum(num)
.takeUnless { it == Visibility.UNKNOWN }
?: account.defaultPostPrivacy)
.takeUnless { it == Visibility.UNLEAKABLE && unleakableAllowed }
?: Visibility.PRIVATE
}
fun stepVisibility() {
visibilityMutable.value = when (visibility.value) {
Visibility.PUBLIC -> Visibility.UNLISTED
Visibility.UNLISTED -> Visibility.PRIVATE
Visibility.PRIVATE -> when (unleakableAllowed) {
true -> Visibility.UNLEAKABLE
false -> Visibility.PUBLIC
}
Visibility.UNLEAKABLE -> Visibility.PUBLIC
else -> Visibility.PUBLIC
}
}
private fun overrideVisibility(overrideTo: Visibility) {
stashedVisibility = visibility.value
visibilityMutable.value = overrideTo
}
fun reply(event: QuickReplyEvent) {
val status = event.status.actionableStatus
inReplyToMutable.value = status
overrideVisibility(status.visibility)
}
fun composeOptions(tootRightNow: Boolean): ComposeActivity.ComposeOptions {
return ComposeActivity.ComposeOptions(
tootText = content.value,
mentionedUsernames = inReplyTo.value
?.let {
linkedSetOf(it.account.username, *(it.mentions.map { mention -> mention.username }.toTypedArray()))
.apply { remove(account.username) }
},
inReplyToId = inReplyTo.value?.id,
visibility = visibility.value,
contentWarning = inReplyTo.value?.spoilerText,
replyingStatusAuthor = inReplyTo.value?.account?.name,
replyingStatusContent = inReplyTo.value?.content?.toString(),
tootRightNow = tootRightNow
)
}
fun reset() {
content.value = ""
inReplyToMutable.value = null
stashedVisibility?.let {
visibilityMutable.value = stashedVisibility
stashedVisibility = null
}
}
}

View File

@ -0,0 +1,59 @@
package net.accelf.yuito
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.observe
import androidx.preference.PreferenceManager
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.Status.Visibility
class VisibilityToggleButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
AppCompatImageView(context, attrs) {
private val preference by lazy { PreferenceManager.getDefaultSharedPreferences(context) }
init {
isClickable = true
isFocusable = true
}
fun attachViewModel(viewModel: QuickTootViewModel, owner: LifecycleOwner) {
viewModel.visibility.observe(owner, ::updateVisibility)
viewModel.setInitialVisibility(preference.getInt(PREF_CURRENT_VISIBILITY, Visibility.UNKNOWN.num))
setOnClickListener{ viewModel.stepVisibility() }
}
private fun updateVisibility(visibility: Visibility) {
setImageResource(
when (visibility) {
Visibility.PUBLIC -> R.drawable.ic_public_24dp
Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp
Visibility.DIRECT -> R.drawable.ic_email_24dp
Visibility.UNLEAKABLE -> R.drawable.ic_low_vision_24dp
else -> R.drawable.ic_lock_open_24dp
}
)
contentDescription = context.getString(
when (visibility) {
Visibility.UNKNOWN -> R.string.visibility_unknown
Visibility.PUBLIC -> R.string.visibility_public
Visibility.UNLISTED -> R.string.visibility_unlisted
Visibility.PRIVATE -> R.string.visibility_private
Visibility.DIRECT -> R.string.visibility_direct
Visibility.UNLEAKABLE -> R.string.visibility_unleakable
}
)
preference.edit()
.putInt(PREF_CURRENT_VISIBILITY, visibility.num)
.apply()
}
companion object {
private const val PREF_CURRENT_VISIBILITY = "current_visibility"
}
}

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,14l5,-5 5,5z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

View File

@ -81,9 +81,8 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<include <net.accelf.yuito.QuickTootView
android:id="@+id/quickTootContainer" android:id="@+id/viewQuickToot"
layout="@layout/view_quick_toot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0" /> android:layout_weight="0" />

View File

@ -35,11 +35,10 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<include <net.accelf.yuito.QuickTootView
android:id="@+id/quick_toot_container" android:id="@+id/viewQuickToot"
layout="@layout/view_quick_toot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0" /> android:layout_weight="0" />
</LinearLayout> </LinearLayout>

View File

@ -35,11 +35,10 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<include <net.accelf.yuito.QuickTootView
android:id="@+id/quick_toot_container" android:id="@+id/viewQuickToot"
layout="@layout/view_quick_toot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0"/> android:layout_weight="0"/>
</LinearLayout> </LinearLayout>

View File

@ -25,11 +25,10 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<include <net.accelf.yuito.QuickTootView
android:id="@+id/quick_toot_container" android:id="@+id/viewQuickToot"
layout="@layout/view_quick_toot"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0" /> android:layout_weight="0" />
</LinearLayout> </LinearLayout>

View File

@ -5,101 +5,32 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageButton
android:id="@+id/button_open_announcements"
style="@style/TuskyImageButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="12dp"
android:layout_marginTop="4dp"
android:padding="4dp"
android:contentDescription="@string/action_more"
android:importantForAccessibility="no"
app:layout_constraintBottom_toTopOf="@+id/quick_reply_info"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_arrow_drop_up" />
<TextView <TextView
android:id="@+id/text_view_announcements" android:id="@+id/textQuickReply"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintBottom_toTopOf="@+id/quick_reply_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/button_open_announcements" />
<ImageButton
android:id="@+id/button_prev_announcements"
style="@style/TuskyImageButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="12dp"
android:layout_marginTop="4dp"
android:padding="4dp"
android:contentDescription="@string/action_more"
android:importantForAccessibility="no"
app:layout_constraintBottom_toTopOf="@+id/text_view_announcements_count"
app:layout_constraintEnd_toStartOf="@id/button_next_announcements"
app:srcCompat="@drawable/ic_chevron_left" />
<ImageButton
android:id="@+id/button_next_announcements"
style="@style/TuskyImageButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="12dp"
android:layout_marginTop="4dp"
android:padding="4dp"
android:contentDescription="@string/action_more"
android:importantForAccessibility="no"
app:layout_constraintBottom_toTopOf="@+id/text_view_announcements_count"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_chevron_right" />
<TextView
android:id="@+id/text_view_announcements_count"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="4dp" app:layout_constraintBottom_toTopOf="@id/editTextContent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintBottom_toTopOf="@id/quick_reply_info"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/quick_reply_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/toot_edit_text"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<TextView <TextView
android:id="@+id/default_tag_info" android:id="@+id/textDefaultTag"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/toot_edit_text" app:layout_constraintBottom_toTopOf="@id/editTextContent"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<ImageView <net.accelf.yuito.VisibilityToggleButton
android:id="@+id/visibility_button" android:id="@+id/buttonVisibility"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:clickable="true"
android:contentDescription="@null"
android:focusable="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent" />
app:srcCompat="@drawable/ic_public_24dp" />
<EditText <EditText
android:id="@+id/toot_edit_text" android:id="@+id/editTextContent"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autofillHints="" android:autofillHints=""
@ -107,11 +38,11 @@
android:inputType="textMultiLine" android:inputType="textMultiLine"
android:minHeight="48dp" android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/toot_button" app:layout_constraintEnd_toStartOf="@id/buttonToot"
app:layout_constraintStart_toEndOf="@id/visibility_button" /> app:layout_constraintStart_toEndOf="@id/buttonVisibility" />
<com.keylesspalace.tusky.components.compose.view.TootButton <com.keylesspalace.tusky.components.compose.view.TootButton
android:id="@+id/toot_button" android:id="@+id/buttonToot"
style="@style/TuskyButton" style="@style/TuskyButton"
android:layout_width="@dimen/toot_button_width" android:layout_width="@dimen/toot_button_width"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -224,6 +224,7 @@
<string name="visibility_private">Followers-Only: Post to followers only</string> <string name="visibility_private">Followers-Only: Post to followers only</string>
<string name="visibility_unleakable">Unleakable: Post to followed user by you only.</string> <string name="visibility_unleakable">Unleakable: Post to followed user by you only.</string>
<string name="visibility_direct">Direct: Post to mentioned users only</string> <string name="visibility_direct">Direct: Post to mentioned users only</string>
<string name="visibility_unknown">Unknown visibility</string>
<string name="pref_title_edit_notification_settings">Notifications</string> <string name="pref_title_edit_notification_settings">Notifications</string>
<string name="pref_title_notifications_enabled">Notifications</string> <string name="pref_title_notifications_enabled">Notifications</string>