Merge branch 'master' into donations

# Conflicts:
#	mastodon/build.gradle
#	mastodon/src/main/res/values/strings.xml
This commit is contained in:
Grishka 2024-04-20 07:16:11 +03:00
commit 79087e3c86
20 changed files with 329 additions and 47 deletions

View File

@ -13,7 +13,7 @@ android {
applicationId "org.joinmastodon.android"
minSdk 23
targetSdk 33
versionCode 96
versionCode 97
versionName "2.5.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View File

@ -23,6 +23,10 @@ import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.transition.ChangeBounds;
import android.transition.Fade;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
@ -32,15 +36,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.TextView;
@ -66,7 +69,9 @@ import org.joinmastodon.android.model.Mention;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.CustomEmojiPopupKeyboard;
import org.joinmastodon.android.ui.ExtendedPopupMenu;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.PopupKeyboard;
@ -87,10 +92,12 @@ import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@ -131,7 +138,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, languageBtn;
private TextView replyText;
private Button visibilityBtn;
private LinearLayout visibilityBtn;
private TextView visibilityText1, visibilityText2, visibilityCurrentText;
private LinearLayout bottomBar;
private View autocompleteDivider;
@ -271,6 +279,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
emojiBtn=view.findViewById(R.id.btn_emoji);
spoilerBtn=view.findViewById(R.id.btn_spoiler);
visibilityBtn=view.findViewById(R.id.btn_visibility);
visibilityText1=view.findViewById(R.id.visibility_text1);
visibilityText2=view.findViewById(R.id.visibility_text2);
visibilityCurrentText=visibilityText1;
languageBtn=view.findViewById(R.id.btn_language);
replyText=view.findViewById(R.id.reply_text);
@ -280,9 +291,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
spoilerBtn.setOnClickListener(v->toggleSpoiler());
languageBtn.setOnClickListener(v->showLanguageAlert());
visibilityBtn.setOnClickListener(this::onVisibilityClick);
visibilityBtn.setAccessibilityDelegate(new View.AccessibilityDelegate(){
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull View host, @NonNull AccessibilityNodeInfo info){
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName("android.widget.Spinner");
}
});
Drawable arrow=getResources().getDrawable(R.drawable.ic_baseline_arrow_drop_down_18, getActivity().getTheme()).mutate();
arrow.setTint(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurface));
visibilityBtn.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, arrow, null);
emojiKeyboard.setOnIconChangedListener(new PopupKeyboard.OnIconChangeListener(){
@Override
public void onIconChanged(int icon){
@ -323,7 +340,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
if(editingStatus!=null && editingStatus.visibility!=null) {
statusVisibility=editingStatus.visibility;
}
updateVisibilityIcon();
updateVisibilityIcon(false);
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
autocompleteViewController.setCompletionSelectedListener(new ComposeAutocompleteViewController.AutocompleteListener(){
@ -909,24 +926,20 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
}
private void onVisibilityClick(View v){
PopupMenu menu=new PopupMenu(getActivity(), v);
menu.inflate(R.menu.compose_visibility);
menu.setOnMenuItemClickListener(item->{
int id=item.getItemId();
if(id==R.id.vis_public){
statusVisibility=StatusPrivacy.PUBLIC;
}else if(id==R.id.vis_unlisted){
statusVisibility=StatusPrivacy.UNLISTED;
}else if(id==R.id.vis_followers){
statusVisibility=StatusPrivacy.PRIVATE;
}else if(id==R.id.vis_private){
statusVisibility=StatusPrivacy.DIRECT;
ArrayList<ListItem<StatusPrivacy>> items=new ArrayList<>();
ExtendedPopupMenu menu=new ExtendedPopupMenu(getActivity(), items);
Consumer<ListItem<StatusPrivacy>> onClick=i->{
if(statusVisibility!=i.parentObject){
statusVisibility=i.parentObject;
updateVisibilityIcon(true);
}
item.setChecked(true);
updateVisibilityIcon();
return true;
});
menu.show();
menu.dismiss();
};
items.add(new ListItem<>(R.string.visibility_public, R.string.visibility_subtitle_public, R.drawable.ic_public_24px, StatusPrivacy.PUBLIC, onClick));
items.add(new ListItem<>(R.string.visibility_unlisted, R.string.visibility_subtitle_unlisted, R.drawable.ic_clear_night_24px, StatusPrivacy.UNLISTED, onClick));
items.add(new ListItem<>(R.string.visibility_followers_only, R.string.visibility_subtitle_followers, R.drawable.ic_lock_24px, StatusPrivacy.PRIVATE, onClick));
items.add(new ListItem<>(R.string.visibility_private, R.string.visibility_subtitle_private, R.drawable.ic_alternate_email_24px, StatusPrivacy.DIRECT, onClick));
menu.showAsDropDown(v);
}
private void loadDefaultStatusVisibility(Bundle savedInstanceState){
@ -960,16 +973,31 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
statusVisibility=(StatusPrivacy) savedInstanceState.getSerializable("visibility");
}
updateVisibilityIcon();
updateVisibilityIcon(false);
}
private void updateVisibilityIcon(){
private void updateVisibilityIcon(boolean animated){
if(getActivity()==null)
return;
if(statusVisibility==null){ // TODO find out why this happens
statusVisibility=StatusPrivacy.PUBLIC;
}
visibilityBtn.setText(switch(statusVisibility){
TextView visibilityText;
if(!animated){
visibilityText=visibilityCurrentText;
}else{
TransitionManager.beginDelayedTransition(visibilityBtn, new TransitionSet()
.addTransition(new Fade(Fade.IN | Fade.OUT))
.addTransition(new ChangeBounds().excludeTarget(TextView.class, true))
.setDuration(250)
.setInterpolator(CubicBezierInterpolator.DEFAULT)
);
visibilityText=visibilityCurrentText==visibilityText1 ? visibilityText2 : visibilityText1;
visibilityText.setVisibility(View.VISIBLE);
visibilityCurrentText.setVisibility(View.GONE);
visibilityCurrentText=visibilityText;
}
visibilityText.setText(switch(statusVisibility){
case PUBLIC -> R.string.visibility_public;
case UNLISTED -> R.string.visibility_unlisted;
case PRIVATE -> R.string.visibility_followers_only;
@ -983,7 +1011,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
}, getActivity().getTheme()).mutate();
icon.setBounds(0, 0, V.dp(18), V.dp(18));
icon.setTint(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary));
visibilityBtn.setCompoundDrawablesRelative(icon, null, visibilityBtn.getCompoundDrawablesRelative()[2], null);
visibilityText.setCompoundDrawablesRelative(icon, null, null, null);
}
@Override

View File

@ -68,6 +68,12 @@ public class ListItem<T>{
this.subtitleRes=subtitleRes;
}
public ListItem(@StringRes int titleRes, @StringRes int subtitleRes, @DrawableRes int iconRes, T parentObject, Consumer<ListItem<T>> onClick){
this(null, null, iconRes, onClick, parentObject, 0, false);
this.titleRes=titleRes;
this.subtitleRes=subtitleRes;
}
public ListItem(@StringRes int titleRes, @StringRes int subtitleRes, @DrawableRes int iconRes, Consumer<ListItem<T>> onClick, int colorOverrideAttr, boolean dividerAfter){
this(null, null, iconRes, onClick, null, colorOverrideAttr, dividerAfter);
this.titleRes=titleRes;

View File

@ -0,0 +1,61 @@
package org.joinmastodon.android.ui;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.adapters.GenericListItemsAdapter;
import org.joinmastodon.android.ui.viewholders.ListItemViewHolder;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class ExtendedPopupMenu extends PopupWindow{
private UsableRecyclerView list;
public <T> ExtendedPopupMenu(Context context, List<ListItem<T>> items){
super(context, null, 0, R.style.Widget_Mastodon_PopupMenu);
setWidth(V.dp(200));
setElevation(V.dp(3));
setOutsideTouchable(true);
setFocusable(true);
setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
list=new UsableRecyclerView(context);
list.setLayoutManager(new LinearLayoutManager(context));
list.setAdapter(new ReducedPaddingItemsAdapter<>(items));
list.setClipToPadding(false);
setContentView(list);
}
@Override
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity){
super.showAsDropDown(anchor, xoff, yoff, gravity);
View bgView=(View) list.getParent();
list.setPadding(0, bgView.getPaddingTop(), 0, bgView.getPaddingBottom());
bgView.setPadding(0, 0, 0, 0);
}
private static class ReducedPaddingItemsAdapter<T> extends GenericListItemsAdapter<T>{
public ReducedPaddingItemsAdapter(List<ListItem<T>> listItems){
super(listItems);
}
@NonNull
@Override
public ListItemViewHolder<?> onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
ListItemViewHolder<?> holder=super.onCreateViewHolder(parent, viewType);
int padH=V.dp(12), padV=V.dp(8);
holder.itemView.setPadding(padH, padV, padH, padV);
View icon=holder.itemView.findViewById(R.id.icon);
((ViewGroup.MarginLayoutParams)icon.getLayoutParams()).setMarginEnd(padH);
return holder;
}
}
}

View File

@ -6,6 +6,7 @@ import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
@ -19,6 +20,9 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.fragments.account_list.StatusFavoritesListFragment;
import org.joinmastodon.android.fragments.account_list.StatusReblogsListFragment;
import org.joinmastodon.android.fragments.account_list.StatusRelatedAccountListFragment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.utils.UiUtils;
@ -48,6 +52,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
private final ImageView share;
private final ColorStateList buttonColors;
private final View replyBtn, boostBtn, favoriteBtn, shareBtn;
private final PopupMenu boostLongTapMenu, favoriteLongTapMenu;
private final View.AccessibilityDelegate buttonAccessibilityDelegate=new View.AccessibilityDelegate(){
@Override
@ -97,11 +102,20 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
replyBtn.setOnClickListener(this::onReplyClick);
replyBtn.setAccessibilityDelegate(buttonAccessibilityDelegate);
boostBtn.setOnClickListener(this::onBoostClick);
boostBtn.setOnLongClickListener(this::onBoostLongClick);
boostBtn.setAccessibilityDelegate(buttonAccessibilityDelegate);
favoriteBtn.setOnClickListener(this::onFavoriteClick);
favoriteBtn.setOnLongClickListener(this::onFavoriteLongClick);
favoriteBtn.setAccessibilityDelegate(buttonAccessibilityDelegate);
shareBtn.setOnClickListener(this::onShareClick);
shareBtn.setAccessibilityDelegate(buttonAccessibilityDelegate);
favoriteLongTapMenu=new PopupMenu(activity, favoriteBtn);
favoriteLongTapMenu.inflate(R.menu.favorite_longtap);
favoriteLongTapMenu.setOnMenuItemClickListener(this::onLongTapMenuItemSelected);
boostLongTapMenu=new PopupMenu(activity, boostBtn);
boostLongTapMenu.inflate(R.menu.boost_longtap);
boostLongTapMenu.setOnMenuItemClickListener(this::onLongTapMenuItemSelected);
}
@Override
@ -172,6 +186,45 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
UiUtils.openSystemShareSheet(v.getContext(), item.status);
}
private boolean onBoostLongClick(View v){
MenuItem boost=boostLongTapMenu.getMenu().findItem(R.id.boost);
boost.setTitle(item.status.reblogged ? R.string.undo_reblog : R.string.button_reblog);
boostLongTapMenu.show();
return true;
}
private boolean onFavoriteLongClick(View v){
MenuItem favorite=favoriteLongTapMenu.getMenu().findItem(R.id.favorite);
MenuItem bookmark=favoriteLongTapMenu.getMenu().findItem(R.id.bookmark);
favorite.setTitle(item.status.favourited ? R.string.undo_favorite : R.string.button_favorite);
bookmark.setTitle(item.status.bookmarked ? R.string.remove_bookmark : R.string.add_bookmark);
favoriteLongTapMenu.show();
return true;
}
private boolean onLongTapMenuItemSelected(MenuItem item){
int id=item.getItemId();
if(id==R.id.favorite){
onFavoriteClick(null);
}else if(id==R.id.boost){
onBoostClick(null);
}else if(id==R.id.bookmark){
AccountSessionManager.getInstance().getAccount(this.item.accountID).getStatusInteractionController().setBookmarked(this.item.status, !this.item.status.bookmarked);
}else if(id==R.id.view_favorites){
startAccountListFragment(StatusFavoritesListFragment.class);
}else if(id==R.id.view_boosts){
startAccountListFragment(StatusReblogsListFragment.class);
}
return true;
}
private void startAccountListFragment(Class<? extends StatusRelatedAccountListFragment> cls){
Bundle args=new Bundle();
args.putString("account", item.parentFragment.getAccountID());
args.putParcelable("status", Parcels.wrap(item.status));
Nav.go(item.parentFragment.getActivity(), cls, args);
}
private int descriptionForId(int id){
if(id==R.id.reply_btn)
return R.string.button_reply;

View File

@ -119,6 +119,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
private final TextView name, timeAndUsername, extraText;
private final ImageView avatar, more;
private final PopupMenu optionsMenu;
private final View clickableThing;
public Holder(Activity activity, ViewGroup parent){
this(activity, R.layout.display_item_header, parent);
@ -131,7 +132,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
avatar=findViewById(R.id.avatar);
more=findViewById(R.id.more);
extraText=findViewById(R.id.extra_text);
avatar.setOnClickListener(this::onAvaClick);
clickableThing=findViewById(R.id.clickable_thing);
clickableThing.setOnClickListener(this::onAvaClick);
avatar.setOutlineProvider(OutlineProviders.roundedRect(10));
avatar.setClipToOutline(true);
more.setOnClickListener(this::onMoreClick);
@ -262,7 +264,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
time=item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt));
timeAndUsername.setText(time+" · @"+item.user.acct);
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : V.dp(4));
if(TextUtils.isEmpty(item.extraText)){
extraText.setVisibility(View.GONE);
}else{
@ -270,8 +272,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
extraText.setText(item.extraText);
}
more.setVisibility(item.inset ? View.GONE : View.VISIBLE);
avatar.setClickable(!item.inset);
avatar.setContentDescription(item.parentFragment.getString(R.string.avatar_description, item.user.acct));
clickableThing.setClickable(!item.inset);
clickableThing.setContentDescription(item.parentFragment.getString(R.string.avatar_description, item.user.acct));
}
@Override

View File

@ -125,7 +125,8 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
public void onBind(NotificationHeaderStatusDisplayItem item){
text.setText(item.text);
avatar.setVisibility(item.notification.type==Notification.Type.POLL ? View.GONE : View.VISIBLE);
// TODO use real icons
if(item.notification.type!=Notification.Type.POLL)
avatar.setContentDescription(item.parentFragment.getString(R.string.avatar_description, item.notification.account.acct));
icon.setImageResource(switch(item.notification.type){
case FAVORITE -> R.drawable.ic_star_fill1_24px;
case REBLOG -> R.drawable.ic_repeat_fill1_24px;

View File

@ -94,7 +94,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
text.setTextIsSelectable(item.textSelectable);
text.setInvalidateOnEveryFrame(false);
itemView.setClickable(false);
text.setPadding(text.getPaddingLeft(), item.reduceTopPadding ? V.dp(8) : V.dp(16), text.getPaddingRight(), text.getPaddingBottom());
text.setPadding(text.getPaddingLeft(), item.reduceTopPadding ? V.dp(8) : V.dp(12), text.getPaddingRight(), text.getPaddingBottom());
text.setTextColor(UiUtils.getThemeColor(text.getContext(), item.inset ? R.attr.colorM3OnSurfaceVariant : R.attr.colorM3OnSurface));
updateTranslation(false);
}

View File

@ -69,6 +69,7 @@ public class PhotoViewerInfoSheet extends BottomSheet{
backButton.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
backButton.setElevation(V.dp(2));
backButton.setAlpha(0f);
backButton.setContentDescription(context.getString(R.string.back));
backButton.setOnClickListener(v->{
listener.onDismissEntireViewer();
dismiss();
@ -82,6 +83,7 @@ public class PhotoViewerInfoSheet extends BottomSheet{
infoButton.setElevation(V.dp(2));
infoButton.setAlpha(0f);
infoButton.setSelected(true);
infoButton.setContentDescription(context.getString(R.string.info));
infoButton.setOnClickListener(v->dismiss());
FrameLayout.LayoutParams lp=new FrameLayout.LayoutParams(V.dp(48), V.dp(48));

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator
android:duration="100"
android:propertyName="alpha"
android:valueTo="0.85"
android:valueFrom="1.0"
android:valueType="floatType"/>
<objectAnimator
android:duration="100"
android:propertyName="textColor"
android:valueTo="#80ffffff"
android:valueFrom="#fff"
android:valueType="colorType"/>
</set>
</item>
<item>
<set>
<objectAnimator
android:duration="200"
android:propertyName="alpha"
android:valueTo="1.0"
android:valueFrom="0.85"
android:valueType="floatType"/>
<objectAnimator
android:duration="200"
android:propertyName="textColor"
android:valueTo="#fff"
android:valueFrom="#80ffffff"
android:valueType="colorType"/>
</set>
</item>
</selector>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/m3_primary_overlay">
<item android:id="@android:id/mask">
<shape>
<solid android:color="#000"/>
<corners android:radius="14dp"/>
</shape>
</item>
</ripple>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,20Q13.35,20 14.613,19.562Q15.875,19.125 16.925,18.3Q15.475,17.775 13.863,16.612Q12.25,15.45 11.025,13.7Q9.8,11.95 9.238,9.637Q8.675,7.325 9.275,4.5Q6.95,5.325 5.475,7.362Q4,9.4 4,12Q4,15.325 6.338,17.663Q8.675,20 12,20ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,8.325 4.312,5.525Q6.625,2.725 10.5,2.125Q11.225,2 11.538,2.462Q11.85,2.925 11.575,3.675Q10.825,5.775 11.05,7.912Q11.275,10.05 12.275,11.85Q13.275,13.65 14.963,14.962Q16.65,16.275 18.825,16.75Q19.625,16.925 19.837,17.425Q20.05,17.925 19.575,18.475Q18.2,20.1 16.238,21.05Q14.275,22 12,22Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M6,22Q5.175,22 4.588,21.413Q4,20.825 4,20V10Q4,9.175 4.588,8.587Q5.175,8 6,8H7V6Q7,3.925 8.463,2.462Q9.925,1 12,1Q14.075,1 15.538,2.462Q17,3.925 17,6V8H18Q18.825,8 19.413,8.587Q20,9.175 20,10V20Q20,20.825 19.413,21.413Q18.825,22 18,22ZM9,8H15V6Q15,4.75 14.125,3.875Q13.25,3 12,3Q10.75,3 9.875,3.875Q9,4.75 9,6ZM6,20H18Q18,20 18,20Q18,20 18,20V10Q18,10 18,10Q18,10 18,10H6Q6,10 6,10Q6,10 6,10V20Q6,20 6,20Q6,20 6,20ZM12,17Q12.825,17 13.413,16.413Q14,15.825 14,15Q14,14.175 13.413,13.587Q12.825,13 12,13Q11.175,13 10.588,13.587Q10,14.175 10,15Q10,15.825 10.588,16.413Q11.175,17 12,17ZM12,15Q12,15 12,15Q12,15 12,15Q12,15 12,15Q12,15 12,15Q12,15 12,15Q12,15 12,15Q12,15 12,15Q12,15 12,15Z"/>
</vector>

View File

@ -14,5 +14,6 @@
android:includeFontPadding="false"
android:background="@drawable/bg_image_alt_overlay"
android:text="ALT"
android:stateListAnimator="@animator/alt_badge"
tools:ignore="HardcodedText"
tools:showIn="@layout/display_item_photo" />

View File

@ -4,8 +4,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingRight="16dp"
android:paddingLeft="16dp">
android:paddingHorizontal="16dp"
android:paddingBottom="4dp"
android:clipToPadding="false">
<ImageView
android:id="@+id/more"
@ -21,6 +22,18 @@
android:contentDescription="@string/more_options"
android:src="@drawable/ic_more_vert_20px" />
<View
android:id="@+id/clickable_thing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="-4dp"
android:layout_marginVertical="-4dp"
android:layout_alignLeft="@id/avatar"
android:layout_alignRight="@id/time_and_username"
android:layout_alignTop="@id/avatar"
android:layout_alignBottom="@id/avatar"
android:background="@drawable/bg_status_header"/>
<ImageView
android:id="@+id/avatar"
android:layout_width="40dp"
@ -28,7 +41,8 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp" />
android:layout_marginEnd="8dp"
android:importantForAccessibility="no"/>
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
android:id="@+id/name_wrap"
@ -70,6 +84,8 @@
android:layout_height="20dp"
android:layout_below="@id/name_wrap"
android:layout_toEndOf="@id/avatar"
android:layout_toStartOf="@id/more"
android:layout_marginEnd="8dp"
android:singleLine="true"
android:ellipsize="end"
android:textAppearance="@style/m3_body_medium"

View File

@ -77,20 +77,49 @@
android:textColor="?colorM3OnSurfaceVariant"
tools:text="\@Gargron" />
<Button
<LinearLayout
android:id="@+id/btn_visibility"
android:layout_width="wrap_content"
android:layout_height="28dp"
android:layout_below="@id/name"
android:layout_toEndOf="@id/avatar"
android:layout_marginTop="8dp"
android:textAppearance="@style/m3_label_large"
android:background="@drawable/bg_filter_chip"
android:textColor="?colorM3OnSurfaceVariant"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:drawablePadding="8dp"
tools:text="@string/visibility_public"/>
android:paddingHorizontal="8dp"
android:orientation="horizontal">
<TextView
android:id="@+id/visibility_text1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorM3OnSurfaceVariant"
android:drawablePadding="8dp"
android:singleLine="true"
android:gravity="center"
tools:text="@string/visibility_public"/>
<TextView
android:id="@+id/visibility_text2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorM3OnSurfaceVariant"
android:drawablePadding="8dp"
android:singleLine="true"
android:gravity="center"
android:visibility="gone"
tools:text="@string/visibility_public"/>
<View
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginStart="8dp"
android:layout_gravity="center_vertical"
android:background="@drawable/ic_baseline_arrow_drop_down_18"
android:backgroundTint="?colorM3OnSurface"/>
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/boost" android:title="@string/button_reblog"/>
<item android:id="@+id/view_boosts" android:title="@string/view_boosts"/>
</menu>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/favorite" android:title="@string/button_favorite"/>
<item android:id="@+id/bookmark" android:title="@string/add_bookmark"/>
<item android:id="@+id/view_favorites" android:title="@string/view_favorites"/>
</menu>

View File

@ -171,8 +171,8 @@
<string name="save">Save</string>
<string name="add_alt_text">Add alt text</string>
<string name="visibility_public">Public</string>
<string name="visibility_followers_only">Followers only</string>
<string name="visibility_private">Only people mentioned</string>
<string name="visibility_followers_only">Followers</string>
<string name="visibility_private">Specific people</string>
<string name="recent_searches">Recents</string>
<string name="skip">Skip</string>
<string name="notification_type_follow">New followers</string>
@ -314,7 +314,7 @@
<string name="instance_signup_closed">This server does not accept new registrations.</string>
<string name="text_copied">Copied to clipboard</string>
<string name="add_bookmark">Bookmark</string>
<string name="remove_bookmark">Remove bookmark</string>
<string name="remove_bookmark">Remove Bookmark</string>
<string name="bookmarks">Bookmarks</string>
<string name="your_favorites">Your favorites</string>
<string name="login_title">Welcome back</string>
@ -747,6 +747,14 @@
<string name="notifications_from_user">Notifications from %s</string>
<string name="notifications_muted">Notifications from %s have been dismissed.</string>
<string name="notifications_allowed">%s will now appear in your notification list.</string>
<string name="visibility_subtitle_public">Everyone on and off Mastodon</string>
<string name="visibility_subtitle_unlisted">Fewer algorithmic fanfares</string>
<string name="visibility_subtitle_followers">Only your followers</string>
<string name="visibility_subtitle_private">Everyone mentioned in the post</string>
<string name="view_boosts">View Boosts</string>
<string name="view_favorites">View Favorites</string>
<string name="undo_reblog">Undo Boost</string>
<string name="undo_favorite">Undo Favorite</string>
<string name="dismiss">Dismiss</string>
<string name="donation_once">Just once</string>
<string name="donation_monthly">Monthly</string>

View File

@ -211,6 +211,7 @@
<item name="android:popupBackground">@drawable/bg_popup</item>
<item name="android:background">@drawable/bg_spinner</item>
<item name="android:backgroundTint">?colorM3OnSurface</item>
<item name="android:popupElevation">3dp</item>
</style>
<style name="Theme.Mastodon.Dialog.Alert" parent="android:Theme.Material.Light.Dialog.Alert">
@ -253,6 +254,7 @@
<style name="Widget.Mastodon.PopupMenu" parent="android:Widget.Material.Light.PopupMenu">
<item name="android:popupBackground">@drawable/bg_popup</item>
<item name="android:popupElevation">3dp</item>
</style>
<style name="Widget.Mastodon.M3.Button" parent="android:Widget.Material.Button">