added status history viewer, layout update, bug fix

This commit is contained in:
nuclearfog 2023-10-23 13:43:30 +02:00
parent 6124810047
commit 11512a303f
No known key found for this signature in database
GPG Key ID: 03488A185C476379
34 changed files with 891 additions and 75 deletions

View File

@ -12,8 +12,8 @@ android {
applicationId 'org.nuclearfog.twidda'
minSdkVersion 21
targetSdkVersion 34
versionCode 100
versionName '3.4.4'
versionCode 101
versionName '3.4.5'
resConfigs 'en', 'es', 'de-rDE', 'zh-rCN'
}
@ -48,7 +48,7 @@ android {
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.recyclerview:recyclerview:1.3.1'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'

View File

@ -130,6 +130,11 @@
android:screenOrientation="portrait"
android:theme="@style/AppTheme" />
<activity
android:name=".ui.activities.EditHistoryActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme" />
<receiver
android:name=".notification.PushNotificationReceiver"
android:enabled="true"

View File

@ -32,7 +32,7 @@ import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.model.lists.Notifications;
import org.nuclearfog.twidda.model.lists.Rules;
import org.nuclearfog.twidda.model.lists.ScheduledStatuses;
import org.nuclearfog.twidda.model.lists.StatusEditHistoy;
import org.nuclearfog.twidda.model.lists.StatusEditHistory;
import org.nuclearfog.twidda.model.lists.Statuses;
import org.nuclearfog.twidda.model.lists.UserLists;
import org.nuclearfog.twidda.model.lists.Users;
@ -290,7 +290,7 @@ public interface Connection {
* @param id ID of the status
* @return list of edited posts
*/
StatusEditHistoy getStatusEditHistory(long id) throws ConnectionException;
StatusEditHistory getStatusEditHistory(long id) throws ConnectionException;
/**
* get trending hashtags

View File

@ -65,7 +65,7 @@ import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.model.lists.Notifications;
import org.nuclearfog.twidda.model.lists.Rules;
import org.nuclearfog.twidda.model.lists.ScheduledStatuses;
import org.nuclearfog.twidda.model.lists.StatusEditHistoy;
import org.nuclearfog.twidda.model.lists.StatusEditHistory;
import org.nuclearfog.twidda.model.lists.Statuses;
import org.nuclearfog.twidda.model.lists.UserLists;
import org.nuclearfog.twidda.model.lists.Users;
@ -476,7 +476,7 @@ public class Mastodon implements Connection {
else if (settings.getPublicTimeline().equals(GlobalSettings.TIMELINE_REMOTE))
params.add("remote=true");
if (search.matches("#\\S+")) {
return getStatuses(ENDPOINT_HASHTAG_TIMELINE + search.substring(1), params, minId, maxId);
return getStatuses(ENDPOINT_HASHTAG_TIMELINE + StringUtils.encode(search.substring(1)), params, minId, maxId);
} else {
params.add("q=" + StringUtils.encode(search));
params.add("type=statuses");
@ -497,9 +497,9 @@ public class Mastodon implements Connection {
@Override
public StatusEditHistoy getStatusEditHistory(long id) throws ConnectionException {
public StatusEditHistory getStatusEditHistory(long id) throws ConnectionException {
try {
StatusEditHistoy result = new StatusEditHistoy();
StatusEditHistory result = new StatusEditHistory();
Response response = get(ENDPOINT_STATUS + id + "/history", new ArrayList<>());
ResponseBody body = response.body();
if (response.code() == 200 && body != null) {

View File

@ -8,6 +8,7 @@ import org.json.JSONObject;
import org.nuclearfog.twidda.backend.utils.StringUtils;
import org.nuclearfog.twidda.model.EditedStatus;
import org.nuclearfog.twidda.model.Emoji;
import org.nuclearfog.twidda.model.Location;
import org.nuclearfog.twidda.model.Media;
import org.nuclearfog.twidda.model.Poll;
import org.nuclearfog.twidda.model.User;
@ -38,9 +39,8 @@ public class EditedMastodonStatus implements EditedStatus {
JSONArray mediaArray = json.optJSONArray("media_attachments");
JSONArray emojiArray = json.optJSONArray("emojis");
String content = json.optString("content", "");
String spoilerText = json.optString("spoiler_text", "");
text = StringUtils.extractText(content);
spoiler = !content.equals(spoilerText);
spoiler = json.optBoolean("spoiler_text", false);
sensitive = json.optBoolean("sensitive", false);
timestamp = StringUtils.getIsoTime(json.optString("created_at"));
author = new MastodonUser(json.getJSONObject("account"), currentUserId);
@ -112,4 +112,11 @@ public class EditedMastodonStatus implements EditedStatus {
public Emoji[] getEmojis() {
return emojis;
}
@Nullable
@Override
public Location getLocation() {
return null;
}
}

View File

@ -0,0 +1,55 @@
package org.nuclearfog.twidda.backend.async;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.api.Connection;
import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.api.ConnectionManager;
import org.nuclearfog.twidda.model.lists.StatusEditHistory;
/**
* Async loader for {@link org.nuclearfog.twidda.ui.fragments.EditHistoryFragment}
*
* @author nuclearfog
*/
public class EditHistoryLoader extends AsyncExecutor<Long, EditHistoryLoader.Result> {
private Connection connection;
/**
*
*/
public EditHistoryLoader(Context context) {
connection = ConnectionManager.getDefaultConnection(context);
}
@Override
protected Result doInBackground(@NonNull Long param) {
try {
StatusEditHistory history = connection.getStatusEditHistory(param);
return new Result(history, null);
} catch (ConnectionException exception) {
return new Result(null, exception);
}
}
/**
*
*/
public static class Result {
@Nullable
public final StatusEditHistory history;
@Nullable
public final ConnectionException exception;
Result(@Nullable StatusEditHistory history, @Nullable ConnectionException exception) {
this.history = history;
this.exception = exception;
}
}
}

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
*
* @author nuclearfog
*/
public interface EditedStatus extends Serializable {
public interface EditedStatus extends Serializable, Comparable<EditedStatus> {
/**
* @return timestamp of this revision
@ -51,4 +51,18 @@ public interface EditedStatus extends Serializable {
* @return array of emojis used in the text
*/
Emoji[] getEmojis();
/**
* @return location associated with this status
*/
@Nullable
Location getLocation();
/**
*
*/
@Override
default int compareTo(EditedStatus status) {
return Long.compare(status.getTimestamp(), getTimestamp());
}
}

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
*
* @author nuclearfog
*/
public interface ScheduledStatus extends Serializable {
public interface ScheduledStatus extends Serializable, Comparable<ScheduledStatus> {
/**
* @return ID of the scheduled status
@ -56,4 +56,11 @@ public interface ScheduledStatus extends Serializable {
* @return true if status contains spoiler information
*/
boolean isSpoiler();
/**
*
*/
default int compareTo(ScheduledStatus status) {
return Long.compare(status.getId(), getId());
}
}

View File

@ -1,5 +1,7 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.model.Account;
import java.util.LinkedList;
@ -24,4 +26,11 @@ public class Accounts extends LinkedList<Account> {
public Accounts(Accounts accounts) {
super(accounts);
}
@NonNull
@Override
public String toString() {
return "item_count=" + size();
}
}

View File

@ -1,5 +1,7 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import java.util.LinkedList;
/**
@ -69,12 +71,35 @@ public class Domains extends LinkedList<String> {
super.addAll(index, list);
}
/**
* get cursor for next items
* get previous cursor of this list
*
* @return cursor or 0L if not set
*/
public long getPreviousCursor() {
return prevCursor;
}
/**
* get next cursor of this list
*
* @return cursor or 0L if not set
*/
public long getNextCursor() {
return nextCursor;
}
@Override
@NonNull
public String toString() {
int itemCount = 0;
for (String item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor();
}
}

View File

@ -33,6 +33,6 @@ public class Fields extends LinkedList<Field> {
@NonNull
@Override
public String toString() {
return "size=" + size();
return "item_count=" + size();
}
}

View File

@ -1,5 +1,7 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.model.Filter;
import java.util.LinkedList;
@ -15,4 +17,11 @@ public class Filters extends LinkedList<Filter> {
public Filters() {
super();
}
@NonNull
@Override
public String toString() {
return "item_count=" + size();
}
}

View File

@ -101,6 +101,12 @@ public class Hashtags extends LinkedList<Hashtag> {
@Override
@NonNull
public String toString() {
return "size=" + size() + " min_id=" + prevCursor + " max_id=" + nextCursor;
int itemCount = 0;
for (Hashtag item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor();
}
}

View File

@ -1,5 +1,7 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.model.Notification;
import java.util.LinkedList;
@ -24,4 +26,17 @@ public class Notifications extends LinkedList<Notification> {
public Notifications(Notifications notifications) {
super(notifications);
}
@NonNull
@Override
public String toString() {
int itemCount = 0;
for (Notification item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount;
}
}

View File

@ -1,5 +1,7 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.model.Rule;
import java.util.ArrayList;
@ -23,4 +25,11 @@ public class Rules extends ArrayList<Rule> {
public Rules(int cap) {
super(cap);
}
@NonNull
@Override
public String toString() {
return "item_count=" + size();
}
}

View File

@ -1,5 +1,6 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.model.ScheduledStatus;
@ -28,4 +29,17 @@ public class ScheduledStatuses extends LinkedList<ScheduledStatus> {
public ScheduledStatus get(int index) {
return super.get(index);
}
@NonNull
@Override
public String toString() {
int itemCount = 0;
for (ScheduledStatus item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount;
}
}

View File

@ -0,0 +1,35 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.model.EditedStatus;
import java.util.LinkedList;
/**
* @author nuclearfog
*/
public class StatusEditHistory extends LinkedList<EditedStatus> {
private static final long serialVersionUID = 6241896565923670373L;
/**
*
*/
public StatusEditHistory() {
}
/**
*
*/
public StatusEditHistory(StatusEditHistory items) {
super(items);
}
@NonNull
@Override
public String toString() {
return "item_count=" + size();
}
}

View File

@ -1,13 +0,0 @@
package org.nuclearfog.twidda.model.lists;
import org.nuclearfog.twidda.model.EditedStatus;
import java.util.LinkedList;
/**
* @author nuclearfog
*/
public class StatusEditHistoy extends LinkedList<EditedStatus> {
private static final long serialVersionUID = 6241896565923670373L;
}

View File

@ -1,5 +1,6 @@
package org.nuclearfog.twidda.model.lists;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.model.Status;
@ -134,4 +135,17 @@ public class Statuses extends LinkedList<Status> {
prevCursor = statuses.getPreviousCursor();
nextCursor = statuses.getNextCursor();
}
@NonNull
@Override
public String toString() {
int itemCount = 0;
for (Status item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount + " prev=" + getPreviousCursor() + " next=" + getNextCursor();
}
}

View File

@ -52,11 +52,20 @@ public class UserLists extends LinkedList<UserList> {
}
/**
* get next link to a list
* get previous cursor of this list
*
* @return cursor
*/
public long getNext() {
public long getPreviousCursor() {
return prevCursor;
}
/**
* get next cursor of this list
*
* @return cursor
*/
public long getNextCursor() {
return nextCursor;
}
@ -111,6 +120,12 @@ public class UserLists extends LinkedList<UserList> {
@Override
@NonNull
public String toString() {
return "size=" + size() + " previous=" + prevCursor + " next=" + nextCursor;
int itemCount = 0;
for (UserList item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor();
}
}

View File

@ -54,7 +54,16 @@ public class Users extends LinkedList<User> {
}
/**
* get next link to a list
* get previous cursor of this list
*
* @return cursor
*/
public long getPreviousCursor() {
return prevCursor;
}
/**
* get next cursor of this list
*
* @return cursor
*/
@ -96,6 +105,12 @@ public class Users extends LinkedList<User> {
@Override
@NonNull
public String toString() {
return "size=" + size() + " previous=" + prevCursor + " next=" + nextCursor;
int itemCount = 0;
for (User item : this) {
if (item != null) {
itemCount++;
}
}
return "item_count=" + itemCount + " previous=" + getPreviousCursor() + " next=" + getNextCursor();
}
}

View File

@ -0,0 +1,38 @@
package org.nuclearfog.twidda.ui.activities;
import android.os.Bundle;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.FragmentTransaction;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.ui.fragments.EditHistoryFragment;
/**
* @author nuclearfog
*/
public class EditHistoryActivity extends AppCompatActivity {
public static final String KEY_ID = EditHistoryFragment.KEY_ID;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.page_fragment);
ViewGroup root = findViewById(R.id.page_fragment_root);
Toolbar toolbar = findViewById(R.id.page_fragment_toolbar);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.page_fragment_container, EditHistoryFragment.class, getIntent().getExtras());
fragmentTransaction.commit();
toolbar.setTitle(R.string.toolbar_status_history);
setSupportActionBar(toolbar);
AppStyles.setTheme(root);
}
}

View File

@ -163,11 +163,12 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
private AudioPlayerDialog audioDialog;
private ReportDialog reportDialog;
private TextView status_source, created_at, status_text, screen_name, username, location_name, sensitive, visibility, spoiler, spoiler_hint, translate_text;
private TextView status_source, created_at, status_text, screen_name, username, edited;
private TextView location_name, sensitive, visibility, spoiler, spoiler_hint, translate_text;
private Button reply_button, repost_button, like_button, reply_name, repost_name_button;
private ImageView profile_image;
private Toolbar toolbar;
private View card_container;
private RecyclerView card_list;
@Nullable
private Status status;
@ -187,7 +188,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
super.onCreate(savedInstanceState);
setContentView(R.layout.page_status);
ViewGroup root = findViewById(R.id.page_status_root);
RecyclerView card_list = findViewById(R.id.page_status_cards);
card_list = findViewById(R.id.page_status_cards);
toolbar = findViewById(R.id.page_status_toolbar);
reply_button = findViewById(R.id.page_status_reply);
repost_button = findViewById(R.id.page_status_repost);
@ -203,10 +204,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
sensitive = findViewById(R.id.page_status_sensitive);
spoiler = findViewById(R.id.page_status_spoiler);
visibility = findViewById(R.id.page_status_visibility);
edited = findViewById(R.id.page_status_edited);
repost_name_button = findViewById(R.id.page_status_reposter_reference);
translate_text = findViewById(R.id.page_status_text_translate);
spoiler_hint = findViewById(R.id.page_status_text_sensitive_hint);
card_container = findViewById(R.id.page_status_cards_container);
//card_container = findViewById(R.id.page_status_cards_container);
clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
statusLoader = new StatusAction(this);
@ -229,6 +231,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
visibility.setCompoundDrawablesWithIntrinsicBounds(R.drawable.global, 0, 0, 0);
reply_name.setCompoundDrawablesWithIntrinsicBounds(R.drawable.back, 0, 0, 0);
repost_name_button.setCompoundDrawablesWithIntrinsicBounds(R.drawable.repost, 0, 0, 0);
edited.setCompoundDrawablesWithIntrinsicBounds(R.drawable.edit, 0, 0, 0);
status_text.setMovementMethod(LinkAndScrollMovement.getInstance());
status_text.setLinkTextColor(settings.getHighlightColor());
card_list.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
@ -498,7 +501,9 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
}
// get edit history
else if (item.getItemId() == R.id.menu_status_history) {
// todo implement history viewer
Intent intent = new Intent(this, EditHistoryActivity.class);
intent.putExtra(EditHistoryActivity.KEY_ID, status.getId());
startActivity(intent);
return true;
}
}
@ -844,6 +849,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
} else {
sensitive.setVisibility(View.GONE);
}
if (status.editedAt() != 0L) {
edited.setVisibility(View.VISIBLE);
} else {
edited.setVisibility(View.GONE);
}
// set status spoiler warning
if (status.isSpoiler()) {
spoiler.setVisibility(View.VISIBLE);
@ -895,11 +905,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
}
// set status attachment preview
if ((status.getCards().length > 0 || status.getMedia().length > 0) || status.getPoll() != null) {
card_container.setVisibility(View.VISIBLE);
card_list.setVisibility(View.VISIBLE);
adapter.replaceAll(status, settings.hideSensitiveEnabled());
status_text.setMaxLines(5);
} else {
card_container.setVisibility(View.GONE);
card_list.setVisibility(View.GONE);
status_text.setMaxLines(10);
}
}

View File

@ -0,0 +1,62 @@
package org.nuclearfog.twidda.ui.adapter.recyclerview;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.nuclearfog.twidda.model.lists.StatusEditHistory;
import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.EditHistoryHolder;
/**
* RecyclerView adapter for {@link org.nuclearfog.twidda.ui.fragments.EditHistoryFragment}
*
* @author nuclearfog
*/
public class EditHistoryAdapter extends RecyclerView.Adapter<EditHistoryHolder> {
private StatusEditHistory items = new StatusEditHistory();
@NonNull
@Override
public EditHistoryHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new EditHistoryHolder(parent);
}
@Override
public void onBindViewHolder(@NonNull EditHistoryHolder holder, int position) {
holder.setContent(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
/**
*
*/
public void setItems(StatusEditHistory items) {
this.items.clear();
this.items.addAll(items);
notifyDataSetChanged();
}
/**
*
*/
public StatusEditHistory getItems() {
return new StatusEditHistory(items);
}
/**
*
*/
public void clear() {
items.clear();
notifyDataSetChanged();
}
}

View File

@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import org.nuclearfog.twidda.model.EditedStatus;
import org.nuclearfog.twidda.model.Media;
import org.nuclearfog.twidda.model.Status;
import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.IconHolder;
@ -99,6 +100,23 @@ public class IconAdapter extends Adapter<IconHolder> implements OnHolderClickLis
notifyDataSetChanged();
}
/**
* add icons using edited status information
*/
public void setItems(EditedStatus status) {
items.clear();
if (status.getMedia().length > 0) {
addMediaIcons(status.getMedia());
}
if (status.getLocation() != null) {
items.add(IconHolder.TYPE_LOCATION);
}
if (status.getPoll() != null) {
items.add(IconHolder.TYPE_POLL);
}
notifyDataSetChanged();
}
/**
* set media icons
*/

View File

@ -108,7 +108,7 @@ public class UserlistAdapter extends Adapter<ViewHolder> implements OnHolderClic
@Override
public boolean onPlaceholderClick(int index) {
boolean actionPerformed = listener.onPlaceholderClick(userlists.getNext(), index);
boolean actionPerformed = listener.onPlaceholderClick(userlists.getNextCursor(), index);
if (actionPerformed)
loadingIndex = index;
return actionPerformed;
@ -133,17 +133,17 @@ public class UserlistAdapter extends Adapter<ViewHolder> implements OnHolderClic
disableLoading();
if (index < 0) {
userlists.replaceAll(newUserlists);
if (userlists.getNext() != 0L) {
if (userlists.getNextCursor() != 0L) {
// Add placeholder
userlists.add(null);
}
notifyDataSetChanged();
} else {
userlists.addAll(index, newUserlists);
if (userlists.getNext() != 0L && userlists.peekLast() != null) {
if (userlists.getNextCursor() != 0L && userlists.peekLast() != null) {
userlists.add(null);
notifyItemRangeInserted(index, newUserlists.size() + 1);
} else if (userlists.getNext() == 0L && userlists.peekLast() == null) {
} else if (userlists.getNextCursor() == 0L && userlists.peekLast() == null) {
userlists.pollLast();
notifyItemRangeInserted(index, newUserlists.size() - 1);
} else {
@ -159,7 +159,7 @@ public class UserlistAdapter extends Adapter<ViewHolder> implements OnHolderClic
*/
public void replaceItems(UserLists newUserlists) {
userlists.replaceAll(newUserlists);
if (userlists.getNext() != 0L && userlists.peekLast() != null) {
if (userlists.getNextCursor() != 0L && userlists.peekLast() != null) {
// Add placeholder
userlists.add(null);
}

View File

@ -0,0 +1,191 @@
package org.nuclearfog.twidda.ui.adapter.recyclerview.holder;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Transformation;
import org.nuclearfog.tag.Tagger;
import org.nuclearfog.textviewtool.LinkAndScrollMovement;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.async.AsyncExecutor;
import org.nuclearfog.twidda.backend.async.TextEmojiLoader;
import org.nuclearfog.twidda.backend.image.PicassoBuilder;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.EmojiUtils;
import org.nuclearfog.twidda.backend.utils.StringUtils;
import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.model.EditedStatus;
import org.nuclearfog.twidda.model.User;
import org.nuclearfog.twidda.ui.adapter.recyclerview.IconAdapter;
import java.util.Random;
import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
/**
* View holder for {@link org.nuclearfog.twidda.ui.adapter.recyclerview.EditHistoryAdapter}
*
* @author nuclearfog
*/
public class EditHistoryHolder extends ViewHolder {
private static final int EMPTY_COLOR = 0x2F000000;
private static final int IMG_SIZE = 150;
private static final Random RND = new Random();
private ImageView profile, icon_lock;
private RecyclerView icon_list;
private TextView username, screen_name, text, spoiler, sensitive, timestamp;
private GlobalSettings settings;
private Picasso picasso;
private TextEmojiLoader emojiLoader;
private Drawable placeholder;
private IconAdapter adapter;
private long tagId = RND.nextLong();
private AsyncExecutor.AsyncCallback<TextEmojiLoader.Result> textResult = this::setTextEmojis;
private AsyncExecutor.AsyncCallback<TextEmojiLoader.Result> usernameResult = this::setUsernameEmojis;
/**
*
*/
public EditHistoryHolder(ViewGroup parent) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_edit_status, parent, false));
settings = GlobalSettings.get(parent.getContext());
picasso = PicassoBuilder.get(parent.getContext());
placeholder = new ColorDrawable(EMPTY_COLOR);
emojiLoader = new TextEmojiLoader(parent.getContext());
adapter = new IconAdapter(null, false);
CardView card = (CardView) itemView;
ViewGroup container = itemView.findViewById(R.id.item_edit_status_container);
profile = itemView.findViewById(R.id.item_edit_status_profile);
username = itemView.findViewById(R.id.item_edit_status_username);
screen_name = itemView.findViewById(R.id.item_edit_status_screenname);
text = itemView.findViewById(R.id.item_edit_status_text);
spoiler = itemView.findViewById(R.id.item_edit_status_spoiler);
sensitive = itemView.findViewById(R.id.item_edit_status_sensitive);
icon_list = itemView.findViewById(R.id.item_edit_status_attachments);
icon_lock = itemView.findViewById(R.id.item_edit_status_locked_icon);
timestamp = itemView.findViewById(R.id.item_edit_status_created_at);
spoiler.setCompoundDrawablesWithIntrinsicBounds(R.drawable.exclamation, 0, 0, 0);
sensitive.setCompoundDrawablesWithIntrinsicBounds(R.drawable.sensitive, 0, 0, 0);
icon_list.setLayoutManager(new LinearLayoutManager(parent.getContext(), LinearLayoutManager.HORIZONTAL, false));
icon_list.setAdapter(adapter);
text.setMovementMethod(LinkAndScrollMovement.getInstance());
AppStyles.setTheme(container, Color.TRANSPARENT);
card.setCardBackgroundColor(settings.getCardColor());
}
/**
* set view content
*
* @param editedStatus edited status content
*/
public void setContent(EditedStatus editedStatus) {
User author = editedStatus.getAuthor();
// set profile image
if (settings.imagesEnabled() && !author.getProfileImageThumbnailUrl().isEmpty()) {
Transformation roundCorner = new RoundedCornersTransformation(2, 0);
picasso.load(author.getProfileImageThumbnailUrl()).transform(roundCorner).resize(IMG_SIZE, IMG_SIZE)
.placeholder(placeholder).centerCrop().error(R.drawable.no_image).into(profile);
} else {
profile.setImageDrawable(placeholder);
}
// set status text and emojis
if (!editedStatus.getText().trim().isEmpty()) {
Spannable textSpan = Tagger.makeTextWithLinks(editedStatus.getText(), settings.getHighlightColor());
if (editedStatus.getEmojis().length > 0 && settings.imagesEnabled()) {
TextEmojiLoader.Param param = new TextEmojiLoader.Param(tagId, editedStatus.getEmojis(), textSpan, text.getResources().getDimensionPixelSize(R.dimen.item_status_icon_size));
emojiLoader.execute(param, textResult);
textSpan = EmojiUtils.removeTags(textSpan);
}
text.setText(textSpan);
text.setVisibility(View.VISIBLE);
} else {
text.setVisibility(View.GONE);
}
// set username and emojis
if (author.getEmojis().length > 0 && !author.getUsername().trim().isEmpty() && settings.imagesEnabled()) {
SpannableString usernameSpan = new SpannableString(author.getUsername());
TextEmojiLoader.Param param = new TextEmojiLoader.Param(tagId, author.getEmojis(), usernameSpan, username.getResources().getDimensionPixelSize(R.dimen.item_status_icon_size));
emojiLoader.execute(param, usernameResult);
username.setText(EmojiUtils.removeTags(usernameSpan));
} else {
username.setText(author.getUsername());
}
if (author.isProtected()) {
icon_lock.setVisibility(View.VISIBLE);
} else {
icon_lock.setVisibility(View.GONE);
}
if (editedStatus.isSpoiler()) {
spoiler.setVisibility(View.VISIBLE);
} else {
spoiler.setVisibility(View.GONE);
}
if (editedStatus.isSensitive()) {
sensitive.setVisibility(View.VISIBLE);
} else {
sensitive.setVisibility(View.GONE);
}
// setup attachment indicators
if (settings.statusIndicatorsEnabled()) {
icon_list.setVisibility(View.VISIBLE);
adapter.setItems(editedStatus);
if (adapter.isEmpty()) {
icon_list.setVisibility(View.GONE);
} else {
icon_list.setVisibility(View.VISIBLE);
}
} else {
icon_list.setVisibility(View.GONE);
}
screen_name.setText(author.getScreenname());
timestamp.setText(StringUtils.formatCreationTime(timestamp.getResources(), editedStatus.getTimestamp()));
}
/**
* update username
*
* @param result username with emojis
*/
private void setUsernameEmojis(@NonNull TextEmojiLoader.Result result) {
if (result.id == tagId && result.images != null) {
Spannable spannable = EmojiUtils.addEmojis(username.getContext(), result.spannable, result.images);
username.setText(spannable);
}
}
/**
* update status text
*
* @param result status text with emojis
*/
private void setTextEmojis(@NonNull TextEmojiLoader.Result result) {
if (result.id == tagId && result.images != null) {
Spannable spannable = EmojiUtils.addEmojis(text.getContext(), result.spannable, result.images);
text.setText(spannable);
}
}
}

View File

@ -0,0 +1,90 @@
package org.nuclearfog.twidda.ui.fragments;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.async.EditHistoryLoader;
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
import org.nuclearfog.twidda.model.lists.StatusEditHistory;
import org.nuclearfog.twidda.ui.adapter.recyclerview.EditHistoryAdapter;
/**
* Status edit history fragment
*
* @author nuclearfog
* @see org.nuclearfog.twidda.ui.activities.EditHistoryActivity
*/
public class EditHistoryFragment extends ListFragment implements AsyncCallback<EditHistoryLoader.Result> {
public static final String KEY_ID = "status-id";
private static final String KEY_DATA = "history-save";
private EditHistoryLoader historyLoader;
private EditHistoryAdapter adapter;
private long id;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
historyLoader = new EditHistoryLoader(requireContext());
adapter = new EditHistoryAdapter();
setAdapter(adapter);
if (getArguments() != null) {
id = getArguments().getLong(KEY_ID);
}
if (savedInstanceState != null) {
Object data = savedInstanceState.getSerializable(KEY_DATA);
if (data instanceof StatusEditHistory) {
adapter.setItems((StatusEditHistory) data);
}
} else {
historyLoader.execute(id, this);
setRefresh(true);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putSerializable(KEY_DATA, adapter.getItems());
super.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
historyLoader.cancel();
super.onDestroy();
}
@Override
protected void onReload() {
historyLoader.execute(id, this);
}
@Override
protected void onReset() {
adapter.clear();
historyLoader.execute(id, this);
}
@Override
public void onResult(@NonNull EditHistoryLoader.Result result) {
if (result.history != null) {
adapter.setItems(result.history);
} else {
ErrorUtils.showErrorMessage(requireContext(), result.exception);
}
setRefresh(false);
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16sp"
android:height="16sp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M17.561,2.439c-1.442,-1.443 -2.525,-1.227 -2.525,-1.227L8.984,7.264L2.21,14.037L1.2,18.799l4.763,-1.01l6.774,-6.771l6.052,-6.052C18.788,4.966 19.005,3.883 17.561,2.439zM5.68,17.217l-1.624,0.35c-0.156,-0.293 -0.345,-0.586 -0.69,-0.932c-0.346,-0.346 -0.639,-0.533 -0.932,-0.691l0.35,-1.623l0.47,-0.469c0,0 0.883,0.018 1.881,1.016c0.997,0.996 1.016,1.881 1.016,1.881L5.68,17.217z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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="wrap_content"
style="@style/CardViewStyle">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/item_edit_status_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/item_status_edit_layout_padding">
<ImageView
android:id="@+id/item_edit_status_profile"
android:layout_width="@dimen/item_status_edit_profile_image_size"
android:layout_height="@dimen/item_status_edit_profile_image_size"
android:layout_margin="@dimen/item_status_edit_layout_margins"
android:contentDescription="@string/profile_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/item_edit_status_username"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_status_edit_padding_drawable"
android:layout_marginStart="@dimen/item_status_edit_layout_margins"
android:layout_marginBottom="@dimen/item_status_edit_layout_margins"
android:layout_marginEnd="@dimen/item_status_edit_layout_margins"
android:textSize="@dimen/item_status_edit_textsize_small"
android:lines="1"
app:layout_constraintStart_toEndOf="@id/item_edit_status_profile"
app:layout_constraintTop_toTopOf="@id/item_edit_status_profile"
app:layout_constraintBottom_toTopOf="@id/item_edit_status_screenname"
app:layout_constraintEnd_toStartOf="@id/item_edit_status_created_at" />
<TextView
android:id="@+id/item_edit_status_created_at"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/item_status_edit_layout_margins"
android:lines="1"
android:textSize="@dimen/item_status_edit_textsize_small"
app:layout_constraintStart_toEndOf="@id/item_edit_status_username"
app:layout_constraintTop_toTopOf="@id/item_edit_status_username"
app:layout_constraintBottom_toBottomOf="@id/item_edit_status_username"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/item_edit_status_locked_icon"
android:layout_width="@dimen/item_status_edit_icon_size"
android:layout_height="@dimen/item_status_edit_icon_size"
android:layout_marginStart="@dimen/item_status_edit_padding_drawable"
android:src="@drawable/lock"
app:layout_constraintStart_toEndOf="@id/item_edit_status_profile"
app:layout_constraintTop_toTopOf="@id/item_edit_status_screenname"
app:layout_constraintBottom_toBottomOf="@id/item_edit_status_screenname"
app:layout_constraintEnd_toStartOf="@id/item_edit_status_screenname"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/item_edit_status_screenname"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_status_edit_padding_drawable"
android:layout_marginStart="@dimen/item_status_edit_layout_margins"
android:layout_marginEnd="@dimen/item_status_edit_layout_margins"
android:textSize="@dimen/item_status_edit_textsize_small"
android:lines="1"
app:layout_constraintStart_toEndOf="@id/item_edit_status_locked_icon"
app:layout_constraintTop_toBottomOf="@id/item_edit_status_username"
app:layout_constraintBottom_toBottomOf="@id/item_edit_status_profile"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/item_edit_status_profile_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="item_edit_status_screenname,item_edit_status_profile"
app:barrierDirection="bottom" />
<TextView
android:id="@+id/item_edit_status_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="10"
android:layout_margin="@dimen/item_status_edit_layout_margins"
android:textSize="@dimen/item_status_edit_textsize_status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/item_edit_status_profile_barrier"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/item_edit_status_sensitive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_status_edit_padding_drawable"
android:text="@string/status_sensitive_media"
android:layout_marginStart="@dimen/item_status_edit_layout_margins"
android:layout_marginTop="@dimen/item_status_edit_layout_margins"
android:textSize="@dimen/item_status_edit_textsize_small"
android:lines="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/item_edit_status_text"
app:layout_constraintEnd_toStartOf="@id/item_edit_status_spoiler" />
<TextView
android:id="@+id/item_edit_status_spoiler"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_status_edit_padding_drawable"
android:text="@string/status_contains_spoiler"
android:layout_marginStart="@dimen/item_status_edit_layout_margins"
android:layout_marginTop="@dimen/item_status_edit_layout_margins"
android:layout_marginEnd="@dimen/item_status_edit_layout_margins"
android:textSize="@dimen/item_status_edit_textsize_small"
android:lines="1"
app:layout_constraintStart_toEndOf="@id/item_edit_status_sensitive"
app:layout_constraintTop_toBottomOf="@id/item_edit_status_text"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/item_edit_status_barrier_warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="item_edit_status_sensitive,item_edit_status_spoiler"
app:barrierDirection="bottom" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/item_edit_status_attachments"
android:layout_width="wrap_content"
android:layout_height="@dimen/item_status_edit_attachment_list_height"
android:layout_margin="@dimen/item_status_edit_layout_margins"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/item_edit_status_barrier_warning"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -174,39 +174,43 @@
app:layout_constraintStart_toStartOf="@id/page_status_text"
app:layout_constraintTop_toBottomOf="@id/page_status_text" />
<LinearLayout
android:id="@+id/page_status_cards_container"
android:layout_width="0dp"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/page_status_cards"
android:layout_width="wrap_content"
android:layout_height="@dimen/page_status_card_height"
android:layout_margin="@dimen/page_status_cards_list_margin"
android:orientation="horizontal"
android:padding="@dimen/page_status_cards_list_padding"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/page_status_text_translate"
app:layout_constraintEnd_toEndOf="parent"
android:gravity="center">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/page_status_cards"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/page_status_visibility"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:visibility="invisible"
android:drawablePadding="@dimen/page_status_padding_drawable"
android:layout_marginStart="@dimen/page_status_textview_margin"
android:layout_marginTop="@dimen/page_status_textview_margin"
android:layout_marginEnd="@dimen/page_status_textview_margin"
android:lines="1"
android:textSize="@dimen/page_status_textsize_small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/page_status_cards_container"
app:layout_constraintEnd_toStartOf="@id/page_status_sensitive"
app:layout_constraintHorizontal_bias="0.0" />
app:layout_constraintTop_toBottomOf="@id/page_status_cards"
app:layout_constraintEnd_toStartOf="@id/page_status_edited" />
<TextView
android:id="@+id/page_status_edited"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:drawablePadding="@dimen/page_status_padding_drawable"
android:layout_marginStart="@dimen/page_status_textview_margin"
android:layout_marginEnd="@dimen/page_status_textview_margin"
android:lines="1"
android:textSize="@dimen/page_status_textsize_small"
android:text="@string/status_edited"
app:layout_constraintStart_toEndOf="@id/page_status_visibility"
app:layout_constraintTop_toBottomOf="@id/page_status_cards"
app:layout_constraintEnd_toStartOf="@id/page_status_sensitive" />
<TextView
android:id="@+id/page_status_sensitive"
@ -215,15 +219,13 @@
android:visibility="invisible"
android:drawablePadding="@dimen/page_status_padding_drawable"
android:layout_marginStart="@dimen/page_status_textview_margin"
android:layout_marginTop="@dimen/page_status_textview_margin"
android:layout_marginEnd="@dimen/page_status_textview_margin"
android:lines="1"
android:text="@string/status_sensitive_media"
android:textSize="@dimen/page_status_textsize_small"
app:layout_constraintStart_toEndOf="@id/page_status_visibility"
app:layout_constraintTop_toBottomOf="@id/page_status_cards_container"
app:layout_constraintStart_toEndOf="@id/page_status_edited"
app:layout_constraintTop_toBottomOf="@id/page_status_cards"
app:layout_constraintEnd_toStartOf="@id/page_status_spoiler"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed" />
<TextView
@ -233,20 +235,18 @@
android:visibility="invisible"
android:drawablePadding="@dimen/page_status_padding_drawable"
android:layout_marginStart="@dimen/page_status_textview_margin"
android:layout_marginTop="@dimen/page_status_textview_margin"
android:layout_marginEnd="@dimen/page_status_textview_margin"
android:lines="1"
android:text="@string/status_contains_spoiler"
android:textSize="@dimen/page_status_textsize_small"
app:layout_constraintStart_toEndOf="@id/page_status_sensitive"
app:layout_constraintTop_toBottomOf="@id/page_status_cards_container"
app:layout_constraintHorizontal_bias="0.0" />
app:layout_constraintTop_toBottomOf="@id/page_status_cards" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/page_status_location_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="page_status_visibility,page_status_sensitive,page_status_spoiler"
app:constraint_referenced_ids="page_status_visibility,page_status_edited,page_status_sensitive,page_status_spoiler"
app:barrierDirection="bottom" />
<TextView

View File

@ -120,6 +120,7 @@
<string name="confirm_unfollow_list">Liste entfolgen?</string>
<string name="trend_range">\u0020Posts</string>
<string name="error_connection_failed">Verbindung fehlgeschlagen!</string>
<string name="status_edited">bearbeitet</string>
<string name="enter_username">Nutzernamen eingeben</string>
<string name="profile_banner">Profilbanner</string>
<string name="editprofile_add_banner">Banner einfügen</string>
@ -157,6 +158,7 @@
<string name="menu_open_filter">Filter verwalten</string>
<string name="menu_open_settings">Einstellungen</string>
<string name="menu_create_list">erstelle Nutzerliste</string>
<string name="toolbar_status_history">Bearbeitungsverlauf</string>
<string name="time_now">jetzt</string>
<string name="menu_follow_user">folgen</string>
<string name="menu_goto_lists">Nutzerlisten</string>

View File

@ -39,7 +39,7 @@
<dimen name="page_status_textsize_small">12sp</dimen>
<dimen name="page_status_textsize">18sp</dimen>
<dimen name="page_status_textsize_locale">12sp</dimen>
<dimen name="page_status_cards_list_margin">5dp</dimen>
<dimen name="page_status_cards_list_padding">5dp</dimen>
<dimen name="page_status_card_height">130sp</dimen>
<dimen name="page_status_icon_size">17sp</dimen>
<integer name="page_status_text_max_lines">5</integer>
@ -266,12 +266,22 @@
<dimen name="item_option_layout_padding">5dp</dimen>
<dimen name="item_option_emoji_size">13sp</dimen>
<!--dimens of item_option_edit.xml -->
<!--dimens of item_option_edit.xml-->
<dimen name="item_option_edit_layout_padding">3dp</dimen>
<dimen name="item_option_edit_button_size">24dp</dimen>
<dimen name="item_option_edit_drawable_padding">2dp</dimen>
<dimen name="item_option_edit_layout_margin">5dp</dimen>
<!--dimens of item_status_edit.xml-->
<dimen name="item_status_edit_profile_image_size">36sp</dimen>
<dimen name="item_status_edit_padding_drawable">5dp</dimen>
<dimen name="item_status_edit_attachment_list_height">24dp</dimen>
<dimen name="item_status_edit_layout_margins">5dp</dimen>
<dimen name="item_status_edit_layout_padding">5dp</dimen>
<dimen name="item_status_edit_icon_size">14sp</dimen>
<dimen name="item_status_edit_textsize_small">12sp</dimen>
<dimen name="item_status_edit_textsize_status">15sp</dimen>
<!--dimens of dialog_status.xml-->
<dimen name="dialog_status_textsize_title">20sp</dimen>
<dimen name="dialog_status_textsize_small">11sp</dimen>

View File

@ -267,6 +267,7 @@
<string name="confirm_delete_list">delete userlist?</string>
<string name="confirm_unfollow_list">unfollow list?</string>
<string name="trend_range">\u0020Posts</string>
<string name="status_edited">edited</string>
<string name="enter_username">enter username</string>
<string name="profile_banner">Banner Image</string>
<string name="editprofile_add_banner">add banner</string>
@ -322,6 +323,7 @@
<string name="toolbar_status_favoriter">User favoriting this status</string>
<string name="toolbar_status_liker">User liking this status</string>
<string name="toolbar_schedule_title">scheduled Posts</string>
<string name="toolbar_status_history">Edit history</string>
<string name="time_now">now</string>
<string name="status_replyname_empty">Reply</string>
<string name="status_media_preview">Media preview</string>