Merge branch 'feature/filters_again'

This commit is contained in:
LucasGGamerM 2023-02-01 21:46:50 -03:00
commit 286b642101
17 changed files with 217 additions and 17 deletions

View File

@ -42,6 +42,7 @@ import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer; import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost; import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.utils.UiUtils;
@ -513,6 +514,26 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
updateImagesSpoilerState(status, itemID); updateImagesSpoilerState(status, itemID);
} }
// public void onRevealFilteredClick(TextStatusDisplayItem.Holder holder){
// Status status=holder.getItem().status;
// revealFiltered(status, holder.getItemID());
// }
public void onRevealFilteredClick(WarningFilteredStatusDisplayItem.Holder holder){
Status status=holder.getItem().status;
// revealFiltered(status, holder.getItemID());
}
protected void revealFiltered(Status status, ArrayList<StatusDisplayItem> showedItems){
status.filterRevealed=true;
}
// public void notifyItemsChanged(int adapterPosition){
// adapter.notifyItemChanged(adapterPosition);
// }
public void onVisibilityIconClick(HeaderStatusDisplayItem.Holder holder){ public void onVisibilityIconClick(HeaderStatusDisplayItem.Holder holder){
Status status=holder.getItem().status; Status status=holder.getItem().status;
status.spoilerRevealed=!status.spoilerRevealed; status.spoilerRevealed=!status.spoilerRevealed;
@ -543,6 +564,16 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
public void onGapClick(GapStatusDisplayItem.Holder item){} public void onGapClick(GapStatusDisplayItem.Holder item){}
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warningItem){
int i = warningItem.getAbsoluteAdapterPosition();
displayItems.remove(warningItem.getAbsoluteAdapterPosition());
for(StatusDisplayItem item:warningItem.filteredItems){
displayItems.add(i, item);
i++;
}
adapter.notifyItemChanged(warningItem.getAbsoluteAdapterPosition());
}
public String getAccountID(){ public String getAccountID(){
return accountID; return accountID;
} }

View File

@ -18,6 +18,8 @@ public class Filter extends BaseModel{
@RequiredField @RequiredField
public String id; public String id;
@RequiredField @RequiredField
public String title;
@RequiredField
public String phrase; public String phrase;
public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class); public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class);
public Instant expiresAt; public Instant expiresAt;
@ -61,6 +63,7 @@ public class Filter extends BaseModel{
public String toString(){ public String toString(){
return "Filter{"+ return "Filter{"+
"id='"+id+'\''+ "id='"+id+'\''+
", title='"+title+'\''+
", phrase='"+phrase+'\''+ ", phrase='"+phrase+'\''+
", context="+context+ ", context="+context+
", expiresAt="+expiresAt+ ", expiresAt="+expiresAt+

View File

@ -58,6 +58,8 @@ public class Status extends BaseModel implements DisplayItemsParent{
public boolean bookmarked; public boolean bookmarked;
public boolean pinned; public boolean pinned;
public boolean filterRevealed;
public transient boolean spoilerRevealed; public transient boolean spoilerRevealed;
public transient boolean hasGapAfter; public transient boolean hasGapAfter;
private transient String strippedText; private transient String strippedText;

View File

@ -105,6 +105,7 @@ public class AudioStatusDisplayItem extends StatusDisplayItem{
}else{ }else{
seekBar.setEnabled(false); seekBar.setEnabled(false);
} }
} }
private void onPlayPauseClick(View v){ private void onPlayPauseClick(View v){

View File

@ -153,6 +153,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
bookmark.setSelected(item.status.bookmarked); bookmark.setSelected(item.status.bookmarked);
boost.setEnabled(item.status.visibility==StatusPrivacy.PUBLIC || item.status.visibility==StatusPrivacy.UNLISTED || item.status.visibility==StatusPrivacy.LOCAL boost.setEnabled(item.status.visibility==StatusPrivacy.PUBLIC || item.status.visibility==StatusPrivacy.UNLISTED || item.status.visibility==StatusPrivacy.LOCAL
|| (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id))); || (item.status.visibility==StatusPrivacy.PRIVATE && item.status.account.id.equals(AccountSessionManager.getInstance().getAccount(item.accountID).self.id)));
} }
private void bindButton(TextView btn, long count){ private void bindButton(TextView btn, long count){

View File

@ -177,7 +177,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
})); }));
optionsMenu=new PopupMenu(activity, more); optionsMenu=new PopupMenu(activity, more);
optionsMenu.inflate(R.menu.post); optionsMenu.inflate(R.menu.post);
optionsMenu.setOnMenuItemClickListener(menuItem->{ optionsMenu.setOnMenuItemClickListener(menuItem->{
Account account=item.user; Account account=item.user;
int id=menuItem.getItemId(); int id=menuItem.getItemId();
@ -275,11 +277,29 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
args.putString("profileDisplayUsername", account.getDisplayUsername()); args.putString("profileDisplayUsername", account.getDisplayUsername());
Nav.go(item.parentFragment.getActivity(), ListTimelinesFragment.class, args); Nav.go(item.parentFragment.getActivity(), ListTimelinesFragment.class, args);
} }
if(!item.status.filterRevealed){
this.itemView.setVisibility(View.GONE);
ViewGroup.LayoutParams params = this.itemView.getLayoutParams();
params.height = 0;
params.width = 0;
this.itemView.setLayoutParams(params);
// item.parentFragment.notifyItemsChanged(this.getAbsoluteAdapterPosition());
}
return true; return true;
}); });
UiUtils.enablePopupMenuIcons(activity, optionsMenu); UiUtils.enablePopupMenuIcons(activity, optionsMenu);
} }
// public void setFilteredShown(){
// this.itemView.setVisibility(View.VISIBLE);
// params = this.itemView.getLayoutParams();
// params.height = 0;
// params.width = 0;
// this.itemView.setLayoutParams(params);
// }
private void populateAccountsMenu(Menu menu) { private void populateAccountsMenu(Menu menu) {
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts(); List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
sessions.stream().filter(s -> !s.getID().equals(item.accountID)).forEach(s -> { sessions.stream().filter(s -> !s.getID().equals(item.accountID)).forEach(s -> {
@ -299,20 +319,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
botIcon.setColorFilter(username.getCurrentTextColor()); botIcon.setColorFilter(username.getCurrentTextColor());
separator.setVisibility(View.VISIBLE); separator.setVisibility(View.VISIBLE);
// if(item.user.bot){
// SpannableStringBuilder ssb = new SpannableStringBuilder();
// ssb.append('@'+item.user.acct);
// ssb.append(" ");
// Drawable botIcon=username.getResources().getDrawable(R.drawable.ic_bot, itemView.getContext().getTheme()).mutate();
// botIcon.setBounds(0, 0, botIcon.getIntrinsicWidth(), botIcon.getIntrinsicHeight());
// botIcon.setTint(username.getCurrentTextColor());
// ssb.append(itemView.getContext().getString(R.string.manually_approves_followers), new ImageSpan(botIcon, ImageSpan.ALIGN_BASELINE), 0);
// username.setPaddingRelative(0,0,16,0);
// username.setText(ssb);
// }
// username.setCompoundDrawablesWithIntrinsicBounds(item.user.bot ? R.drawable.ic_fluent_bot_24_filled : 0, 0, 0, 0);
if (item.scheduledStatus!=null) if (item.scheduledStatus!=null)
if (item.scheduledStatus.scheduledAt.isAfter(CreateStatus.DRAFTS_AFTER_INSTANT)) { if (item.scheduledStatus.scheduledAt.isAfter(CreateStatus.DRAFTS_AFTER_INSTANT)) {
timestamp.setText(R.string.sk_draft); timestamp.setText(R.string.sk_draft);
@ -393,6 +399,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
more.setContentDescription(desc); more.setContentDescription(desc);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) more.setTooltipText(desc); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) more.setTooltipText(desc);
} }
@Override @Override

View File

@ -73,6 +73,7 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
photo.setImageDrawable(crossfadeDrawable); photo.setImageDrawable(crossfadeDrawable);
photo.setContentDescription(TextUtils.isEmpty(item.attachment.description) ? item.parentFragment.getString(R.string.media_no_description) : item.attachment.description); photo.setContentDescription(TextUtils.isEmpty(item.attachment.description) ? item.parentFragment.getString(R.string.media_no_description) : item.attachment.description);
didClear=false; didClear=false;
} }
@Override @Override

View File

@ -84,6 +84,7 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
photo.setImageDrawable(crossfadeDrawable); photo.setImageDrawable(crossfadeDrawable);
didClear=false; didClear=false;
} }
} }
@Override @Override

View File

@ -106,6 +106,15 @@ public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
altTextWrapper.setVisibility(View.GONE); altTextWrapper.setVisibility(View.GONE);
} }
} }
if(!item.status.filterRevealed){
this.itemView.setVisibility(View.GONE);
ViewGroup.LayoutParams params = this.itemView.getLayoutParams();
params.height = 0;
params.width = 0;
this.itemView.setLayoutParams(params);
// item.parentFragment.notifyItemsChanged(this.getAbsoluteAdapterPosition());
}
} }
private void onShowHideClick(View v){ private void onShowHideClick(View v){

View File

@ -100,6 +100,7 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
if (visibilityText != 0) text.setContentDescription(item.text + " (" + ctx.getString(visibilityText) + ")"); if (visibilityText != 0) text.setContentDescription(item.text + " (" + ctx.getString(visibilityText) + ")");
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N) if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(text); UiUtils.fixCompoundDrawableTintOnAndroid6(text);
} }
@Override @Override

View File

@ -8,6 +8,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.joinmastodon.android.R; import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment; import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment; import org.joinmastodon.android.fragments.HashtagTimelineFragment;
@ -19,6 +20,7 @@ import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Hashtag; import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Notification; import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Poll; import org.joinmastodon.android.model.Poll;
@ -26,6 +28,7 @@ import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.PhotoLayoutHelper; import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels; import org.parceler.Parcels;
import java.util.ArrayList; import java.util.ArrayList;
@ -78,6 +81,7 @@ public abstract class StatusDisplayItem{
case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent); case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent);
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent); case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
case GAP -> new GapStatusDisplayItem.Holder(activity, parent); case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
case WARNING -> new WarningFilteredStatusDisplayItem.Holder(activity, parent);
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent); case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
}; };
} }
@ -89,11 +93,19 @@ public abstract class StatusDisplayItem{
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate){ public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate){
String parentID=parentObject.getID(); String parentID=parentObject.getID();
ArrayList<StatusDisplayItem> items=new ArrayList<>(); ArrayList<StatusDisplayItem> items=new ArrayList<>();
ArrayList<StatusDisplayItem> filtered=new ArrayList<>();
Status statusForContent=status.getContentStatus(); Status statusForContent=status.getContentStatus();
Bundle args=new Bundle(); Bundle args=new Bundle();
args.putString("account", accountID); args.putString("account", accountID);
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null; ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null;
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.HOME)).collect(Collectors.toList());
StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters);
statusForContent.filterRevealed = filterPredicate.testWithWarning(status);
if(status.reblog!=null){ if(status.reblog!=null){
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account); boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{ items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
@ -126,11 +138,12 @@ public abstract class StatusDisplayItem{
} }
))); )));
} }
HeaderStatusDisplayItem header; HeaderStatusDisplayItem header;
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification, scheduledStatus)); items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification, scheduledStatus));
if(!TextUtils.isEmpty(statusForContent.content)) if(!TextUtils.isEmpty(statusForContent.content)){
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, disableTranslate)); items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, disableTranslate));
else } else
header.needBottomPadding=true; header.needBottomPadding=true;
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList()); List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
if(!imageAttachments.isEmpty()){ if(!imageAttachments.isEmpty()){
@ -162,14 +175,22 @@ public abstract class StatusDisplayItem{
} }
if(addFooter){ if(addFooter){
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID)); items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));
if(status.hasGapAfter && !(fragment instanceof ThreadFragment)) if(status.hasGapAfter && !(fragment instanceof ThreadFragment)){
items.add(new GapStatusDisplayItem(parentID, fragment)); items.add(new GapStatusDisplayItem(parentID, fragment));
} }
}
int i=1; int i=1;
for(StatusDisplayItem item:items){ for(StatusDisplayItem item:items){
item.inset=inset; item.inset=inset;
item.index=i++; item.index=i++;
} }
if(!statusForContent.filterRevealed){
filtered.add(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items));
return filtered;
}
return items; return items;
} }
@ -196,6 +217,7 @@ public abstract class StatusDisplayItem{
ACCOUNT, ACCOUNT,
HASHTAG, HASHTAG,
GAP, GAP,
WARNING,
EXTENDED_FOOTER EXTENDED_FOOTER
} }

View File

@ -156,6 +156,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
? View.VISIBLE : View.GONE); ? View.VISIBLE : View.GONE);
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post); translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, item.translation.provider) : ""); translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, item.translation.provider) : "");
translateButton.setOnClickListener(v->{ translateButton.setOnClickListener(v->{
if (item.translation == null) { if (item.translation == null) {
translateProgress.setVisibility(View.VISIBLE); translateProgress.setVisibility(View.VISIBLE);
@ -186,6 +189,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
rebind(); rebind();
} }
}); });
} }
@Override @Override

View File

@ -0,0 +1,62 @@
package org.joinmastodon.android.ui.displayitems;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.drawables.SawtoothTearDrawable;
import java.util.ArrayList;
// Mind the gap!
public class WarningFilteredStatusDisplayItem extends StatusDisplayItem{
public boolean loading;
public final Status status;
public ArrayList<StatusDisplayItem> filteredItems;
public WarningFilteredStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, ArrayList<StatusDisplayItem> items){
super(parentID, parentFragment);
this.status=status;
this.filteredItems = items;
}
@Override
public Type getType(){
return Type.WARNING;
}
public static class Holder extends StatusDisplayItem.Holder<WarningFilteredStatusDisplayItem>{
public final View warningWrap;
public final Button showBtn;
public final TextView text;
public ArrayList<StatusDisplayItem> filteredItems;
public Holder(Context context, ViewGroup parent){
super(context, R.layout.display_item_warning, parent);
warningWrap=findViewById(R.id.warning_wrap);
showBtn=findViewById(R.id.reveal_btn);
showBtn.setOnClickListener(i -> item.parentFragment.onWarningClick(this));
text=findViewById(R.id.text);
// itemView.setOnClickListener(v->item.parentFragment.onRevealFilteredClick(this));
}
@Override
public void onBind(WarningFilteredStatusDisplayItem item){
filteredItems = item.filteredItems;
text.setText(item.parentFragment.getString(R.string.mo_filtered, item.status.filtered.get(item.status.filtered.size() -1).filter.title));
}
@Override
public void onClick(){
}
}
}

View File

@ -38,4 +38,22 @@ public class StatusFilterPredicate implements Predicate<Status>{
} }
return true; return true;
} }
public boolean testWithWarning(Status status) {
if(status.filtered!=null){
if (status.filtered.isEmpty()){
return true;
}
boolean matches=status.filtered.stream()
.map(filterResult->filterResult.filter)
.filter(filter->filter.expiresAt==null||filter.expiresAt.isAfter(Instant.now()))
.anyMatch(filter->filter.filterAction==Filter.FilterAction.WARN);
return !matches;
}
for(Filter filter:filters){
if(filter.matches(status))
return false;
}
return true;
}
} }

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="75dp"
android:id="@+id/warning_wrap"
android:background="@drawable/bg_timeline_gap">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_gravity="start|center_vertical"
android:textAppearance="@style/m3_title_medium"
android:textColor="?android:textColorSecondary"
android:text="@string/mo_filtered"/>
<Button
android:id="@+id/reveal_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_gravity="end|center_vertical"
android:text="@string/tap_to_reveal"
android:singleLine="true"
android:ellipsize="end"
android:visibility="visible"
/>
</FrameLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp">
</FrameLayout>

View File

@ -22,6 +22,7 @@
<string name="mo_no_image_desc">The included images have no description. Please consider adding one, to allow visually impaired people to participate.</string> <string name="mo_no_image_desc">The included images have no description. Please consider adding one, to allow visually impaired people to participate.</string>
<string name="mo_emoji_recent">Recently used</string> <string name="mo_emoji_recent">Recently used</string>
<string name="mo_clear_recent_emoji">Clear recently used emoji</string> <string name="mo_clear_recent_emoji">Clear recently used emoji</string>
<string name="mo_filtered">Filtered: %s</string>
<string name="mo_disable_relocate_publish_button_to_enable_customization">Disable "Relocate publish button" to allow customization</string> <string name="mo_disable_relocate_publish_button_to_enable_customization">Disable "Relocate publish button" to allow customization</string>
<!-- accessibility labels--> <!-- accessibility labels-->