[quick-toot] Add quick-toot feature

This commit is contained in:
kyori 2018-08-13 09:14:57 +09:00 committed by kyori19
parent 573be935a7
commit ad7ff6d06b
14 changed files with 512 additions and 99 deletions

View File

@ -197,12 +197,13 @@ public final class ComposeActivity
private static final String MEDIA_ATTACHMENTS_EXTRA = "media_attachments";
private static final String SENSITIVE_EXTRA = "sensitive";
private static final String POLL_EXTRA = "poll";
private static final String TOOT_RIGHT_NOW = "toot_right_now";
// Mastodon only counts URLs as this long in terms of status character limits
static final int MAXIMUM_URL_LENGTH = 23;
// https://github.com/tootsuite/mastodon/blob/1656663/app/models/media_attachment.rb#L94
private static final int MEDIA_DESCRIPTION_CHARACTER_LIMIT = 420;
private static final String[] CAN_USE_UNLEAKABLE = {"itabashi.0j0.jp", "odakyu.app"};
public static final String[] CAN_USE_UNLEAKABLE = {"itabashi.0j0.jp", "odakyu.app"};
private static final String[] CAN_USE_QUOTE_ID = {"odakyu.app", "biwakodon.com", "dtp-mstdn.jp", "nitiasa.com"};
@Inject
@ -259,6 +260,7 @@ public final class ComposeActivity
private Integer maxPollOptionLength = null;
private @Px
int thumbnailViewSize;
private boolean tootRightNow = false;
private SaveTootHelper saveTootHelper;
private Gson gson = new Gson();
@ -552,6 +554,8 @@ public final class ComposeActivity
if(mediaAttachments != null && mediaAttachments.size() > 0) {
enablePollButton(false);
}
tootRightNow = intent.getBooleanExtra(TOOT_RIGHT_NOW, false);
}
// After the starting state is finalised, the interface can be set to reflect this state.
@ -594,7 +598,7 @@ public final class ComposeActivity
builder.append(name);
builder.append(' ');
}
startingText = builder.toString();
startingText = builder.toString() + textEditor.getText();
textEditor.setText(startingText);
textEditor.setSelection(textEditor.length());
}
@ -731,6 +735,10 @@ public final class ComposeActivity
}
textEditor.requestFocus();
if (tootRightNow && calculateTextLength() > 0) {
onSendClicked();
}
}
private void replaceTextAtCaret(CharSequence text) {
@ -2119,6 +2127,8 @@ public final class ComposeActivity
private Boolean sensitive;
@Nullable
private NewPoll poll;
@Nullable
private Boolean tootRightNow;
public IntentBuilder savedTootUid(int uid) {
this.savedTootUid = uid;
@ -2200,6 +2210,11 @@ public final class ComposeActivity
return this;
}
public IntentBuilder tootRightNow(boolean tootRightNow) {
this.tootRightNow = tootRightNow;
return this;
}
public Intent build(Context context) {
Intent intent = new Intent(context, ComposeActivity.class);
@ -2252,6 +2267,9 @@ public final class ComposeActivity
if (poll != null) {
intent.putExtra(POLL_EXTRA, poll);
}
if (tootRightNow != null) {
intent.putExtra(TOOT_RIGHT_NOW, tootRightNow);
}
return intent;
}
}

View File

@ -16,26 +16,38 @@
package com.keylesspalace.tusky;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.MenuItem;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.fragment.TimelineFragment;
import net.accelf.yuito.QuickTootHelper;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasAndroidInjector;
import io.reactivex.android.schedulers.AndroidSchedulers;
import static com.uber.autodispose.AutoDispose.autoDisposable;
import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from;
public class FavouritesActivity extends BottomSheetActivity implements HasAndroidInjector {
@Inject
public DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
@Inject
public EventHub eventHub;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@ -55,6 +67,15 @@ public class FavouritesActivity extends BottomSheetActivity implements HasAndroi
Fragment fragment = TimelineFragment.newInstance(TimelineFragment.Kind.FAVOURITES);
fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit();
ConstraintLayout quickTootContainer = findViewById(R.id.quick_toot_container);
QuickTootHelper quickTootHelper = new QuickTootHelper(quickTootContainer,
PreferenceManager.getDefaultSharedPreferences(this), accountManager, eventHub);
eventHub.getEvents()
.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(quickTootHelper::handleEvent);
}
@Override

View File

@ -15,26 +15,11 @@
package com.keylesspalace.tusky;
import androidx.lifecycle.Lifecycle;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import androidx.emoji.text.EmojiCompat;
import androidx.fragment.app.Fragment;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AlertDialog;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
@ -42,6 +27,18 @@ import android.view.KeyEvent;
import android.widget.ImageButton;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.emoji.text.EmojiCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager.widget.ViewPager;
import com.bumptech.glide.Glide;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.keylesspalace.tusky.appstore.CacheUpdater;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent;
@ -72,6 +69,8 @@ import com.mikepenz.materialdrawer.model.interfaces.IProfile;
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader;
import com.mikepenz.materialdrawer.util.DrawerImageLoader;
import net.accelf.yuito.QuickTootHelper;
import java.util.ArrayList;
import java.util.List;
@ -185,10 +184,12 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
tabLayout = findViewById(R.id.tab_layout);
viewPager = findViewById(R.id.pager);
composeButton.setOnClickListener(v -> {
Intent composeIntent = new Intent(getApplicationContext(), ComposeActivity.class);
startActivity(composeIntent);
});
ConstraintLayout quickTootContainer = findViewById(R.id.quick_toot_container);
QuickTootHelper quickTootHelper = new QuickTootHelper(quickTootContainer,
PreferenceManager.getDefaultSharedPreferences(this), accountManager, eventHub);
composeButton.setOnClickListener(v -> quickTootHelper.composeButton());
tabLayout.requestFocus();
setupDrawer();
@ -250,6 +251,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
if (event instanceof MainTabsChangedEvent) {
setupTabs(false);
}
quickTootHelper.handleEvent(event);
});
// Flush old media that was cached for sharing

View File

@ -3,13 +3,21 @@ package com.keylesspalace.tusky
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.preference.PreferenceManager
import android.view.MenuItem
import androidx.constraintlayout.widget.ConstraintLayout
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.interfaces.ActionButtonActivity
import com.uber.autodispose.AutoDispose.autoDisposable
import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.toolbar_basic.*
import net.accelf.yuito.QuickTootHelper
import javax.inject.Inject
class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInjector {
@ -31,6 +39,8 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
@Inject
lateinit var eventHub: EventHub
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -52,6 +62,15 @@ class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasAn
.replace(R.id.contentFrame, TimelineFragment.newInstance(kind, argument))
.commit()
}
val quickTootContainer = findViewById<ConstraintLayout>(R.id.quick_toot_container)
val quickTootHelper = QuickTootHelper(quickTootContainer,
PreferenceManager.getDefaultSharedPreferences(this), accountManager, eventHub)
eventHub.events
.observeOn(AndroidSchedulers.mainThread())
.`as`(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(quickTootHelper::handleEvent)
}
override fun getActionButton(): FloatingActionButton? = null

View File

@ -18,21 +18,31 @@ package com.keylesspalace.tusky;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.MenuItem;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.Lifecycle;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.fragment.TimelineFragment;
import net.accelf.yuito.QuickTootHelper;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasAndroidInjector;
import io.reactivex.android.schedulers.AndroidSchedulers;
import static com.uber.autodispose.AutoDispose.autoDisposable;
import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from;
public class ViewTagActivity extends BottomSheetActivity implements HasAndroidInjector {
@ -40,6 +50,8 @@ public class ViewTagActivity extends BottomSheetActivity implements HasAndroidIn
@Inject
public DispatchingAndroidInjector<Object> dispatchingAndroidInjector;
@Inject
public EventHub eventHub;
public static Intent getIntent(Context context, String tag){
Intent intent = new Intent(context,ViewTagActivity.class);
@ -68,6 +80,15 @@ public class ViewTagActivity extends BottomSheetActivity implements HasAndroidIn
Fragment fragment = TimelineFragment.newInstance(TimelineFragment.Kind.TAG, hashtag);
fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit();
ConstraintLayout quickTootContainer = findViewById(R.id.quick_toot_container);
QuickTootHelper quickTootHelper = new QuickTootHelper(quickTootContainer,
PreferenceManager.getDefaultSharedPreferences(this), accountManager, eventHub);
eventHub.getEvents()
.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe(quickTootHelper::handleEvent);
}
@Override

View File

@ -16,4 +16,5 @@ data class ProfileEditedEvent(val newProfileData: Account) : Dispatchable
data class PreferenceChangedEvent(val preferenceKey: String) : Dispatchable
data class MainTabsChangedEvent(val newTabs: List<TabData>) : Dispatchable
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

View File

@ -38,6 +38,7 @@ import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.FavoriteEvent;
import com.keylesspalace.tusky.appstore.MuteEvent;
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
import com.keylesspalace.tusky.appstore.QuickReplyEvent;
import com.keylesspalace.tusky.appstore.ReblogEvent;
import com.keylesspalace.tusky.appstore.StatusComposedEvent;
import com.keylesspalace.tusky.appstore.StatusDeletedEvent;
@ -565,7 +566,23 @@ public class TimelineFragment extends SFragment implements
@Override
public void onReply(int position) {
super.reply(statuses.get(position).asRight());
switch (kind) {
case HOME:
case PUBLIC_LOCAL:
case PUBLIC_FEDERATED:
case TAG:
case FAVOURITES:
case LIST: {
eventHub.dispatch(new QuickReplyEvent(statuses.get(position).asRight().getActionableStatus()));
break;
}
case USER:
case USER_PINNED:
case USER_WITH_REPLIES: {
super.reply(statuses.get(position).asRight());
break;
}
}
}
@Override

View File

@ -0,0 +1,201 @@
package net.accelf.yuito;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.keylesspalace.tusky.ComposeActivity;
import com.keylesspalace.tusky.R;
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.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Status;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
public class QuickTootHelper {
private Context context;
private TextView quickReplyInfo;
private ImageView visibilityButton;
private EditText tootEditText;
private SharedPreferences defPrefs;
private String domain;
private String loggedInUsername;
private EventHub eventHub;
private Status inReplyTo;
private static final String PREF_CURRENT_VISIBILITY = "current_visibility";
public QuickTootHelper(ConstraintLayout root, SharedPreferences defPrefs, AccountManager accountManager, EventHub eventHub) {
context = root.getContext();
quickReplyInfo = root.findViewById(R.id.quick_reply_info);
visibilityButton = root.findViewById(R.id.visibility_button);
tootEditText = root.findViewById(R.id.toot_edit_text);
Button quickTootButton = root.findViewById(R.id.toot_button);
this.defPrefs = defPrefs;
AccountEntity account = accountManager.getActiveAccount();
if (account != null) {
domain = account.getDomain();
loggedInUsername = account.getUsername();
}
this.eventHub = eventHub;
updateVisibilityButton();
visibilityButton.setOnClickListener(v -> setNextVisibility());
quickTootButton.setOnClickListener(v -> quickToot());
}
public void composeButton() {
if (tootEditText.getText().length() == 0 && inReplyTo == null) {
Intent composeIntent = new Intent(context, ComposeActivity.class);
context.startActivity(composeIntent);
} else {
startComposeWithQuickComposeData();
}
}
public void handleEvent(Event event) {
if (event instanceof QuickReplyEvent) {
reply(((QuickReplyEvent) event).getStatus());
} else if (event instanceof PreferenceChangedEvent) {
if (PREF_CURRENT_VISIBILITY.equals(((PreferenceChangedEvent) event).getPreferenceKey())) {
updateVisibilityButton();
}
}
}
private void reply(Status status) {
inReplyTo = status;
updateQuickReplyInfo();
}
private void startComposeWithQuickComposeData() {
Intent composeIntent = setupIntentBuilder(false);
resetQuickCompose();
context.startActivity(composeIntent);
}
private void quickToot() {
if (tootEditText.getText().toString().length() > 0) {
Intent composeIntent = setupIntentBuilder(true);
resetQuickCompose();
context.startActivity(composeIntent);
}
}
private Intent setupIntentBuilder(boolean tootRightNow) {
ComposeActivity.IntentBuilder intentBuilder = new ComposeActivity.IntentBuilder()
.tootText(tootEditText.getText().toString())
.visibility(getCurrentVisibility())
.tootRightNow(tootRightNow);
if (inReplyTo == null) {
return intentBuilder.build(context);
}
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);
return intentBuilder.inReplyToId(inReplyTo.getId())
.contentWarning(inReplyTo.getSpoilerText())
.mentionedUsernames(mentionedUsernames)
.replyingStatusAuthor(inReplyTo.getAccount().getLocalUsername())
.replyingStatusContent(inReplyTo.getContent().toString())
.build(context);
}
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 Status.Visibility getCurrentVisibility() {
Status.Visibility visibility = Status.Visibility.byNum(defPrefs.getInt(PREF_CURRENT_VISIBILITY, Status.Visibility.PUBLIC.getNum()));
if (!Arrays.asList(ComposeActivity.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();
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_unleakable_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(ComposeActivity.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();
}
}

View File

@ -1,20 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.keylesspalace.tusky.FavouritesActivity">
<include layout="@layout/toolbar_basic" />
<FrameLayout
android:id="@+id/fragment_container"
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
android:layout_weight="1">
<include layout="@layout/item_status_bottom_sheet"/>
<include layout="@layout/toolbar_basic" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<include layout="@layout/item_status_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include
android:id="@+id/quick_toot_container"
layout="@layout/view_quick_toot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"/>
</LinearLayout>

View File

@ -1,66 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.keylesspalace.tusky.MainActivity">
<RelativeLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
android:layout_weight="1" >
<ImageButton
android:id="@+id/drawer_toggle"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="?android:colorBackground"
android:contentDescription="@string/action_open_drawer"
android:elevation="@dimen/actionbar_elevation"
app:srcCompat="@drawable/ic_menu_24dp" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
style="@style/TuskyTabAppearance"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@id/drawer_toggle"
android:background="?android:colorBackground"
android:elevation="@dimen/actionbar_elevation"
app:tabGravity="fill"
app:tabIconTint="@color/tab_icon_color"
app:tabMaxWidth="0dp"
app:tabMode="fixed"
app:tabPaddingEnd="1dp"
app:tabPaddingStart="1dp"
app:tabPaddingTop="4dp"
app:tabUnboundedRipple="false" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
<ImageButton
android:id="@+id/drawer_toggle"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="?android:colorBackground"
android:contentDescription="@string/action_open_drawer"
android:elevation="@dimen/actionbar_elevation"
app:srcCompat="@drawable/ic_menu_24dp" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
style="@style/TuskyTabAppearance"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@id/drawer_toggle"
android:background="?android:colorBackground"
android:focusable="true"
android:focusableInTouchMode="true"
android:elevation="@dimen/actionbar_elevation"
app:tabGravity="fill"
app:tabIconTint="@color/tab_icon_color"
app:tabMaxWidth="0dp"
app:tabMode="fixed"
app:tabPaddingEnd="1dp"
app:tabPaddingStart="1dp"
app:tabPaddingTop="4dp"
app:tabUnboundedRipple="false" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tab_layout"
android:layout_alignParentBottom="true" />
</RelativeLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floating_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tab_layout"
android:layout_alignParentBottom="true" />
android:layout_margin="16dp"
android:contentDescription="@string/action_compose"
app:layout_anchor="@id/pager"
app:layout_anchorGravity="bottom|end"
app:srcCompat="@drawable/ic_create_24dp" />
</RelativeLayout>
<include layout="@layout/item_status_bottom_sheet" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floating_btn"
android:layout_width="wrap_content"
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include layout="@layout/view_quick_toot"
android:id="@+id/quick_toot_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/action_compose"
app:layout_anchor="@id/pager"
app:layout_anchorGravity="bottom|end"
app:srcCompat="@drawable/ic_create_24dp" />
android:layout_weight="0" />
<include layout="@layout/item_status_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@ -1,20 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.keylesspalace.tusky.ModalTimelineActivity">
<include layout="@layout/toolbar_basic" />
<FrameLayout
android:id="@+id/contentFrame"
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
android:layout_weight="1">
<include layout="@layout/item_status_bottom_sheet"/>
<include layout="@layout/toolbar_basic" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<FrameLayout
android:id="@+id/contentFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<include layout="@layout/item_status_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include
android:id="@+id/quick_toot_container"
layout="@layout/view_quick_toot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0" />
</LinearLayout>

View File

@ -1,20 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_view_thread"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.keylesspalace.tusky.ViewTagActivity">
<include layout="@layout/toolbar_basic" />
<FrameLayout
android:id="@+id/fragment_container"
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
android:layout_weight="1">
<include layout="@layout/item_status_bottom_sheet"/>
<include layout="@layout/toolbar_basic" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<include layout="@layout/item_status_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include
android:id="@+id/quick_toot_container"
layout="@layout/view_quick_toot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0" />
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_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" />
<ImageView
android:id="@+id/visibility_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
android:clickable="true"
android:contentDescription="@null"
android:focusable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_public_24dp" />
<EditText
android:id="@+id/toot_edit_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/hint_toot_area"
android:inputType="textMultiLine"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/toot_button"
app:layout_constraintStart_toEndOf="@id/visibility_button"
tools:ignore="Autofill" />
<Button
android:id="@+id/toot_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/action_send_public"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -158,6 +158,7 @@
<string name="hint_display_name">Display name</string>
<string name="hint_note">Bio</string>
<string name="hint_search">Search…</string>
<string name="hint_toot_area">Quick Toot Area</string>
<string name="search_no_results">No results</string>