finalized user field support, restructured ViewPager adapter
This commit is contained in:
parent
211ee4d22c
commit
60df3dd016
|
@ -1,50 +0,0 @@
|
||||||
package org.nuclearfog.twidda.backend.api.mastodon.impl;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
|
||||||
import org.nuclearfog.twidda.model.User.Field;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User fields implementation of Mastodon
|
|
||||||
*
|
|
||||||
* @author nuclearfog
|
|
||||||
*/
|
|
||||||
public class MastodonField implements Field {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 2278113885084330065L;
|
|
||||||
|
|
||||||
private String key;
|
|
||||||
private String value;
|
|
||||||
private long timestamp = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param json fields json
|
|
||||||
*/
|
|
||||||
public MastodonField(JSONObject json) throws JSONException {
|
|
||||||
key = json.getString("name");
|
|
||||||
value = json.getString("value");
|
|
||||||
String timeStr = json.getString("verified_at");
|
|
||||||
if (!timeStr.equals("null")) {
|
|
||||||
timestamp = StringUtils.getTime(timeStr, StringUtils.TIME_MASTODON);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,10 +6,6 @@ import androidx.annotation.Nullable;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.jsoup.Jsoup;
|
|
||||||
import org.jsoup.nodes.Document;
|
|
||||||
import org.jsoup.nodes.Document.OutputSettings;
|
|
||||||
import org.jsoup.safety.Safelist;
|
|
||||||
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
||||||
import org.nuclearfog.twidda.model.Card;
|
import org.nuclearfog.twidda.model.Card;
|
||||||
import org.nuclearfog.twidda.model.Emoji;
|
import org.nuclearfog.twidda.model.Emoji;
|
||||||
|
@ -31,8 +27,6 @@ public class MastodonStatus implements Status {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1184375228249441241L;
|
private static final long serialVersionUID = 1184375228249441241L;
|
||||||
|
|
||||||
private static final OutputSettings OUTPUT_SETTINGS = new OutputSettings().prettyPrint(false);
|
|
||||||
|
|
||||||
private long id;
|
private long id;
|
||||||
private long replyId;
|
private long replyId;
|
||||||
private long replyUserId;
|
private long replyUserId;
|
||||||
|
@ -90,24 +84,7 @@ public class MastodonStatus implements Status {
|
||||||
sensitive = json.optBoolean("sensitive", false);
|
sensitive = json.optBoolean("sensitive", false);
|
||||||
spoiler = json.optBoolean("spoiler_text", false);
|
spoiler = json.optBoolean("spoiler_text", false);
|
||||||
bookmarked = json.optBoolean("bookmarked", false);
|
bookmarked = json.optBoolean("bookmarked", false);
|
||||||
String text = json.optString("content", "");
|
text = StringUtils.extractText(json.optString("content", ""));
|
||||||
|
|
||||||
try {
|
|
||||||
// create newlines at every <br> or <p> tag
|
|
||||||
Document jsoupDoc = Jsoup.parse(text);
|
|
||||||
jsoupDoc.outputSettings(OUTPUT_SETTINGS);
|
|
||||||
jsoupDoc.select("br").after("\\n");
|
|
||||||
jsoupDoc.select("p").before("\\n");
|
|
||||||
String str = jsoupDoc.html().replace("\\n", "\n");
|
|
||||||
text = Jsoup.clean(str, "", Safelist.none(), OUTPUT_SETTINGS);
|
|
||||||
text = text.replace("<", "<").replace(">", ">").replace("&", "&").replace(" ", "\u00A0");
|
|
||||||
if (text.startsWith("\n")) {
|
|
||||||
text = text.substring(1);
|
|
||||||
}
|
|
||||||
} catch (Exception exception) {
|
|
||||||
// use fallback text string from json
|
|
||||||
}
|
|
||||||
this.text = text;
|
|
||||||
|
|
||||||
if (author.getId() != currentUserId)
|
if (author.getId() != currentUserId)
|
||||||
mentions = author.getScreenname() + ' ';
|
mentions = author.getScreenname() + ' ';
|
||||||
|
|
|
@ -248,4 +248,47 @@ public class MastodonUser implements User {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "name=\"" + getScreenname() + "\"";
|
return "name=\"" + getScreenname() + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static class MastodonField implements Field {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2278113885084330065L;
|
||||||
|
|
||||||
|
private String key;
|
||||||
|
private String value;
|
||||||
|
private long timestamp = 0L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param json fields json
|
||||||
|
*/
|
||||||
|
public MastodonField(JSONObject json) throws JSONException {
|
||||||
|
key = json.getString("name");
|
||||||
|
value = StringUtils.extractText(json.optString("value", ""));
|
||||||
|
|
||||||
|
String timeStr = json.getString("verified_at");
|
||||||
|
if (!timeStr.equals("null")) {
|
||||||
|
timestamp = StringUtils.getTime(timeStr, StringUtils.TIME_MASTODON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,9 @@ package org.nuclearfog.twidda.backend.utils;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.nodes.Document;
|
||||||
|
import org.jsoup.safety.Safelist;
|
||||||
import org.nuclearfog.twidda.BuildConfig;
|
import org.nuclearfog.twidda.BuildConfig;
|
||||||
import org.nuclearfog.twidda.R;
|
import org.nuclearfog.twidda.R;
|
||||||
|
|
||||||
|
@ -61,6 +64,8 @@ public class StringUtils {
|
||||||
|
|
||||||
private static final TimeZone TIME_ZONE = TimeZone.getDefault();
|
private static final TimeZone TIME_ZONE = TimeZone.getDefault();
|
||||||
|
|
||||||
|
private static final Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fallback date if parsing failed
|
* fallback date if parsing failed
|
||||||
*/
|
*/
|
||||||
|
@ -194,6 +199,31 @@ public class StringUtils {
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract text from html doc
|
||||||
|
*
|
||||||
|
* @param text html string
|
||||||
|
* @return text string
|
||||||
|
*/
|
||||||
|
public static String extractText(String text) {
|
||||||
|
try {
|
||||||
|
// create newlines at every <br> or <p> tag
|
||||||
|
Document jsoupDoc = Jsoup.parse(text);
|
||||||
|
jsoupDoc.outputSettings(OUTPUT_SETTINGS);
|
||||||
|
jsoupDoc.select("br").after("\\n");
|
||||||
|
jsoupDoc.select("p").before("\\n");
|
||||||
|
String str = jsoupDoc.html().replace("\\n", "\n");
|
||||||
|
text = Jsoup.clean(str, "", Safelist.none(), OUTPUT_SETTINGS);
|
||||||
|
text = text.replace("<", "<").replace(">", ">").replace("&", "&").replace(" ", "\u00A0");
|
||||||
|
if (text.startsWith("\n")) {
|
||||||
|
text = text.substring(1);
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
// use fallback text string from json
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert time strings from different APIs to the local format
|
* convert time strings from different APIs to the local format
|
||||||
*
|
*
|
||||||
|
|
|
@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.nuclearfog.twidda.model.User.Field;
|
import org.nuclearfog.twidda.model.User.Field;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +25,11 @@ public class Fields extends LinkedList<Field> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Fields(Field[] fields) {
|
||||||
|
super(Arrays.asList(fields));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import androidx.viewpager2.widget.ViewPager2;
|
||||||
import org.nuclearfog.twidda.R;
|
import org.nuclearfog.twidda.R;
|
||||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
import org.nuclearfog.twidda.ui.adapter.fragments.FragmentAdapter;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
|
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector;
|
import org.nuclearfog.twidda.ui.views.TabSelector;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
||||||
|
|
|
@ -66,7 +66,7 @@ import org.nuclearfog.twidda.config.Configuration;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.Relation;
|
import org.nuclearfog.twidda.model.Relation;
|
||||||
import org.nuclearfog.twidda.model.User;
|
import org.nuclearfog.twidda.model.User;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
import org.nuclearfog.twidda.ui.adapter.fragments.ProfileAdapter;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||||
import org.nuclearfog.twidda.ui.views.LockableLinearLayout;
|
import org.nuclearfog.twidda.ui.views.LockableLinearLayout;
|
||||||
|
@ -137,7 +137,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
||||||
private AsyncCallback<EmojiResult> usernameUpdate = this::onUsernameUpdate;
|
private AsyncCallback<EmojiResult> usernameUpdate = this::onUsernameUpdate;
|
||||||
private AsyncCallback<EmojiResult> userDescriptionUpdate = this::onUserDescriptionUpdate;
|
private AsyncCallback<EmojiResult> userDescriptionUpdate = this::onUserDescriptionUpdate;
|
||||||
|
|
||||||
private FragmentAdapter adapter;
|
private ProfileAdapter adapter;
|
||||||
private GlobalSettings settings;
|
private GlobalSettings settings;
|
||||||
private Picasso picasso;
|
private Picasso picasso;
|
||||||
private ConfirmDialog confirmDialog;
|
private ConfirmDialog confirmDialog;
|
||||||
|
@ -192,7 +192,7 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
||||||
tabSelector = findViewById(R.id.profile_tab);
|
tabSelector = findViewById(R.id.profile_tab);
|
||||||
viewPager = findViewById(R.id.profile_pager);
|
viewPager = findViewById(R.id.profile_pager);
|
||||||
|
|
||||||
adapter = new FragmentAdapter(this);
|
adapter = new ProfileAdapter(this);
|
||||||
relationLoader = new RelationLoader(this);
|
relationLoader = new RelationLoader(this);
|
||||||
domainAction = new DomainAction(this);
|
domainAction = new DomainAction(this);
|
||||||
userLoader = new UserLoader(this);
|
userLoader = new UserLoader(this);
|
||||||
|
@ -261,13 +261,14 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
||||||
RelationParam param = new RelationParam(userId, RelationParam.LOAD);
|
RelationParam param = new RelationParam(userId, RelationParam.LOAD);
|
||||||
relationLoader.execute(param, relationCallback);
|
relationLoader.execute(param, relationCallback);
|
||||||
}
|
}
|
||||||
adapter.setupProfilePage(userId);
|
adapter.setUser(userId);
|
||||||
if (settings.likeEnabled()) {
|
if (settings.getLogin().getConfiguration() == Configuration.MASTODON && userId != settings.getLogin().getId()) {
|
||||||
|
tabSelector.addTabIcons(R.array.profile_tab_icons);
|
||||||
|
} else if (settings.likeEnabled()) {
|
||||||
tabSelector.addTabIcons(R.array.profile_tab_icons_like);
|
tabSelector.addTabIcons(R.array.profile_tab_icons_like);
|
||||||
} else {
|
} else {
|
||||||
tabSelector.addTabIcons(R.array.profile_tab_icons);
|
tabSelector.addTabIcons(R.array.profile_tab_icons_favorite);
|
||||||
}
|
}
|
||||||
|
|
||||||
tabSelector.addOnTabSelectedListener(this);
|
tabSelector.addOnTabSelectedListener(this);
|
||||||
following.setOnClickListener(this);
|
following.setOnClickListener(this);
|
||||||
follower.setOnClickListener(this);
|
follower.setOnClickListener(this);
|
||||||
|
@ -863,6 +864,9 @@ public class ProfileActivity extends AppCompatActivity implements ActivityResult
|
||||||
emojiLoader.execute(param, userDescriptionUpdate);
|
emojiLoader.execute(param, userDescriptionUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (user.getFields().length > 0) {
|
||||||
|
adapter.setFields(user.getFields());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.config.Configuration;
|
import org.nuclearfog.twidda.config.Configuration;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.Trend;
|
import org.nuclearfog.twidda.model.Trend;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
import org.nuclearfog.twidda.ui.adapter.fragments.FragmentAdapter;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector;
|
import org.nuclearfog.twidda.ui.views.TabSelector;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.UserList;
|
import org.nuclearfog.twidda.model.UserList;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
import org.nuclearfog.twidda.ui.adapter.fragments.FragmentAdapter;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
|
||||||
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector;
|
import org.nuclearfog.twidda.ui.views.TabSelector;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import androidx.viewpager2.widget.ViewPager2;
|
||||||
import org.nuclearfog.twidda.R;
|
import org.nuclearfog.twidda.R;
|
||||||
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
import org.nuclearfog.twidda.ui.adapter.fragments.FragmentAdapter;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector;
|
import org.nuclearfog.twidda.ui.views.TabSelector;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
import org.nuclearfog.twidda.backend.utils.ErrorUtils;
|
||||||
import org.nuclearfog.twidda.config.Configuration;
|
import org.nuclearfog.twidda.config.Configuration;
|
||||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FragmentAdapter;
|
import org.nuclearfog.twidda.ui.adapter.fragments.FragmentAdapter;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector;
|
import org.nuclearfog.twidda.ui.views.TabSelector;
|
||||||
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
import org.nuclearfog.twidda.ui.views.TabSelector.OnTabSelectedListener;
|
||||||
|
|
||||||
|
|
|
@ -5,25 +5,33 @@ import android.view.ViewGroup;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
import androidx.recyclerview.widget.RecyclerView.Adapter;
|
||||||
|
|
||||||
|
import org.nuclearfog.tag.Tagger.OnTagClickListener;
|
||||||
import org.nuclearfog.twidda.model.lists.Fields;
|
import org.nuclearfog.twidda.model.lists.Fields;
|
||||||
import org.nuclearfog.twidda.ui.adapter.holder.FieldHolder;
|
import org.nuclearfog.twidda.ui.adapter.holder.FieldHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* RecyclerView adapter used to show a list of {@link org.nuclearfog.twidda.model.User.Field}
|
||||||
|
*
|
||||||
* @author nuclearfog
|
* @author nuclearfog
|
||||||
*/
|
*/
|
||||||
public class FieldAdapter extends Adapter<FieldHolder> {
|
public class FieldAdapter extends Adapter<FieldHolder> implements OnTagClickListener {
|
||||||
|
|
||||||
|
private OnLinkClickListener listener;
|
||||||
|
|
||||||
private Fields fields = new Fields();
|
private Fields fields = new Fields();
|
||||||
|
|
||||||
public FieldAdapter() {
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public FieldAdapter(OnLinkClickListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public FieldHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public FieldHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
return new FieldHolder(parent);
|
return new FieldHolder(parent, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,14 +47,46 @@ public class FieldAdapter extends Adapter<FieldHolder> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTagClick(String tag) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLinkClick(String link) {
|
||||||
|
listener.onLinkClick(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* replace all existing items with new ones
|
||||||
|
*
|
||||||
|
* @param fields items to insert
|
||||||
|
*/
|
||||||
public void replaceItems(Fields fields) {
|
public void replaceItems(Fields fields) {
|
||||||
this.fields.clear();
|
this.fields.clear();
|
||||||
this.fields.addAll(fields);
|
this.fields.addAll(fields);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all items
|
||||||
|
*
|
||||||
|
* @return Field list
|
||||||
|
*/
|
||||||
public Fields getItems() {
|
public Fields getItems() {
|
||||||
return new Fields(fields);
|
return new Fields(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click listener for url
|
||||||
|
*/
|
||||||
|
public interface OnLinkClickListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called on url click
|
||||||
|
*
|
||||||
|
* @param url url string
|
||||||
|
*/
|
||||||
|
void onLinkClick(String url);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.nuclearfog.twidda.ui.adapter;
|
package org.nuclearfog.twidda.ui.adapter.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package org.nuclearfog.twidda.ui.adapter.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
|
import org.nuclearfog.twidda.model.User;
|
||||||
|
import org.nuclearfog.twidda.model.lists.Fields;
|
||||||
|
import org.nuclearfog.twidda.ui.fragments.FieldFragment;
|
||||||
|
import org.nuclearfog.twidda.ui.fragments.ListFragment;
|
||||||
|
import org.nuclearfog.twidda.ui.fragments.StatusFragment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewPager adapter used to show profile timelines
|
||||||
|
*
|
||||||
|
* @author nuclearfog
|
||||||
|
*/
|
||||||
|
public class ProfileAdapter extends ViewPagerAdapter {
|
||||||
|
|
||||||
|
private GlobalSettings settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ProfileAdapter(FragmentActivity fragmentActivity) {
|
||||||
|
super(fragmentActivity);
|
||||||
|
settings = GlobalSettings.get(fragmentActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create adapter items
|
||||||
|
*
|
||||||
|
* @param userId ID of the user profile
|
||||||
|
*/
|
||||||
|
public void setUser(long userId) {
|
||||||
|
// user timeline
|
||||||
|
Bundle paramUser = new Bundle();
|
||||||
|
paramUser.putLong(StatusFragment.KEY_ID, userId);
|
||||||
|
paramUser.putInt(StatusFragment.KEY_MODE, StatusFragment.MODE_USER);
|
||||||
|
ListFragment statusFragment = new StatusFragment();
|
||||||
|
statusFragment.setArguments(paramUser);
|
||||||
|
// user favorits
|
||||||
|
Bundle paramFavorite = new Bundle();
|
||||||
|
paramFavorite.putLong(StatusFragment.KEY_ID, userId);
|
||||||
|
paramFavorite.putInt(StatusFragment.KEY_MODE, StatusFragment.MODE_FAVORIT);
|
||||||
|
ListFragment favoriteFragment = new StatusFragment();
|
||||||
|
favoriteFragment.setArguments(paramFavorite);
|
||||||
|
// user bookmarks
|
||||||
|
Bundle paramBookmark = new Bundle();
|
||||||
|
paramBookmark.putLong(StatusFragment.KEY_ID, userId);
|
||||||
|
paramBookmark.putInt(StatusFragment.KEY_MODE, StatusFragment.MODE_BOOKMARK);
|
||||||
|
ListFragment bookmarkFragment = new StatusFragment();
|
||||||
|
bookmarkFragment.setArguments(paramBookmark);
|
||||||
|
// user fields
|
||||||
|
ListFragment fieldFragment = new FieldFragment();
|
||||||
|
|
||||||
|
fragments.clear();
|
||||||
|
fragments.add(statusFragment);
|
||||||
|
switch (settings.getLogin().getConfiguration()) {
|
||||||
|
case MASTODON:
|
||||||
|
if (settings.getLogin().getId() == userId) {
|
||||||
|
fragments.add(favoriteFragment);
|
||||||
|
fragments.add(bookmarkFragment);
|
||||||
|
}
|
||||||
|
fragments.add(fieldFragment);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TWITTER1:
|
||||||
|
case TWITTER2:
|
||||||
|
fragments.add(favoriteFragment);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* put user fields into FieldFragment
|
||||||
|
*
|
||||||
|
* @param fields user fields
|
||||||
|
*/
|
||||||
|
public void setFields(User.Field[] fields) {
|
||||||
|
for (ListFragment fragment : fragments) {
|
||||||
|
if (fragment instanceof FieldFragment) {
|
||||||
|
((FieldFragment) fragment).setItems(new Fields(fields));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.nuclearfog.twidda.ui.adapter.fragments;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
|
|
||||||
|
import org.nuclearfog.twidda.ui.fragments.ListFragment;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author nuclearfog
|
||||||
|
*/
|
||||||
|
public class ViewPagerAdapter extends FragmentStateAdapter {
|
||||||
|
|
||||||
|
protected ArrayList<ListFragment> fragments = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public ViewPagerAdapter(FragmentActivity fragmentActivity) {
|
||||||
|
super(fragmentActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final long getItemId(int position) {
|
||||||
|
return fragments.get(position).getSessionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean containsItem(long itemId) {
|
||||||
|
for (ListFragment fragment : fragments) {
|
||||||
|
if (fragment.getSessionId() == itemId)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public final Fragment createFragment(int position) {
|
||||||
|
return fragments.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getItemCount() {
|
||||||
|
return fragments.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called when app settings change
|
||||||
|
*/
|
||||||
|
public void notifySettingsChanged() {
|
||||||
|
for (ListFragment fragment : fragments) {
|
||||||
|
if (!fragment.isDetached()) {
|
||||||
|
fragment.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called to scroll page to top
|
||||||
|
*
|
||||||
|
* @param index tab position of page
|
||||||
|
*/
|
||||||
|
public void scrollToTop(int index) {
|
||||||
|
if (!fragments.get(index).isDetached()) {
|
||||||
|
fragments.get(index).onTabChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,64 @@
|
||||||
package org.nuclearfog.twidda.ui.adapter.holder;
|
package org.nuclearfog.twidda.ui.adapter.holder;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.cardview.widget.CardView;
|
||||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||||
|
|
||||||
|
import org.nuclearfog.tag.Tagger;
|
||||||
|
import org.nuclearfog.tag.Tagger.OnTagClickListener;
|
||||||
import org.nuclearfog.twidda.R;
|
import org.nuclearfog.twidda.R;
|
||||||
|
import org.nuclearfog.twidda.backend.utils.AppStyles;
|
||||||
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
import org.nuclearfog.twidda.backend.utils.StringUtils;
|
||||||
|
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||||
import org.nuclearfog.twidda.model.User.Field;
|
import org.nuclearfog.twidda.model.User.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ViewHolder for {@link org.nuclearfog.twidda.ui.adapter.FieldAdapter}
|
||||||
|
*
|
||||||
|
* @author nuclearfog
|
||||||
|
*/
|
||||||
public class FieldHolder extends ViewHolder {
|
public class FieldHolder extends ViewHolder {
|
||||||
|
|
||||||
private View verified;
|
private View verified;
|
||||||
private TextView key, value, time;
|
private TextView key, value, time;
|
||||||
|
|
||||||
|
private GlobalSettings settings;
|
||||||
|
private OnTagClickListener listener;
|
||||||
|
|
||||||
public FieldHolder(ViewGroup parent) {
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public FieldHolder(ViewGroup parent, OnTagClickListener listener) {
|
||||||
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_field, parent, false));
|
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_field, parent, false));
|
||||||
|
CardView background = (CardView) itemView;
|
||||||
|
ViewGroup container = itemView.findViewById(R.id.item_field_container);
|
||||||
verified = itemView.findViewById(R.id.item_field_verified);
|
verified = itemView.findViewById(R.id.item_field_verified);
|
||||||
key = itemView.findViewById(R.id.item_field_key);
|
key = itemView.findViewById(R.id.item_field_key);
|
||||||
value = itemView.findViewById(R.id.item_field_value);
|
value = itemView.findViewById(R.id.item_field_value);
|
||||||
time = itemView.findViewById(R.id.item_field_timestamp);
|
time = itemView.findViewById(R.id.item_field_timestamp);
|
||||||
|
settings = GlobalSettings.get(parent.getContext());
|
||||||
|
this.listener = listener;
|
||||||
|
|
||||||
|
value.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
background.setCardBackgroundColor(settings.getCardColor());
|
||||||
|
AppStyles.setTheme(container, Color.TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set view content
|
||||||
|
*/
|
||||||
public void setContent(Field field) {
|
public void setContent(Field field) {
|
||||||
key.setText(field.getKey());
|
key.setText(field.getKey());
|
||||||
value.setText(field.getValue());
|
value.setText(Tagger.makeTextWithLinks(field.getValue(), settings.getHighlightColor(), listener));
|
||||||
if (field.getTimestamp() != 0L) {
|
if (field.getTimestamp() != 0L) {
|
||||||
verified.setVisibility(View.VISIBLE);
|
verified.setVisibility(View.VISIBLE);
|
||||||
time.setText(R.string.field_verified_in);
|
time.setText(R.string.field_verified);
|
||||||
time.append(StringUtils.formatCreationTime(time.getResources(), field.getTimestamp()));
|
time.append(StringUtils.formatCreationTime(time.getResources(), field.getTimestamp()));
|
||||||
} else {
|
} else {
|
||||||
verified.setVisibility(View.GONE);
|
verified.setVisibility(View.GONE);
|
||||||
|
|
|
@ -6,10 +6,19 @@ import android.view.View;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.nuclearfog.twidda.backend.utils.LinkUtils;
|
||||||
import org.nuclearfog.twidda.model.lists.Fields;
|
import org.nuclearfog.twidda.model.lists.Fields;
|
||||||
import org.nuclearfog.twidda.ui.adapter.FieldAdapter;
|
import org.nuclearfog.twidda.ui.adapter.FieldAdapter;
|
||||||
|
import org.nuclearfog.twidda.ui.adapter.FieldAdapter.OnLinkClickListener;
|
||||||
|
|
||||||
public class FieldFragment extends ListFragment {
|
/**
|
||||||
|
* User field list fragment
|
||||||
|
*
|
||||||
|
* @author nuclearfog
|
||||||
|
*/
|
||||||
|
public class FieldFragment extends ListFragment implements OnLinkClickListener {
|
||||||
|
|
||||||
|
private static final String KEY_SAVE = "fields-save";
|
||||||
|
|
||||||
private FieldAdapter adapter;
|
private FieldAdapter adapter;
|
||||||
|
|
||||||
|
@ -17,18 +26,32 @@ public class FieldFragment extends ListFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
adapter = new FieldAdapter();
|
adapter = new FieldAdapter(this);
|
||||||
setAdapter(adapter);
|
setAdapter(adapter);
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
Object data = savedInstanceState.getSerializable(KEY_SAVE);
|
||||||
|
if (data instanceof Fields) {
|
||||||
|
adapter.replaceItems((Fields) data);
|
||||||
|
}
|
||||||
|
}
|
||||||
disableSwipe();
|
disableSwipe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
Fields items = adapter.getItems();
|
||||||
|
outState.putSerializable(KEY_SAVE, items);
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLinkClick(String url) {
|
||||||
|
LinkUtils.openLink(requireActivity(), url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onReload() {
|
protected void onReload() {
|
||||||
}
|
}
|
||||||
|
@ -38,7 +61,11 @@ public class FieldFragment extends ListFragment {
|
||||||
protected void onReset() {
|
protected void onReset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set field items
|
||||||
|
*
|
||||||
|
* @param items new items to show in a list
|
||||||
|
*/
|
||||||
public void setItems(Fields items) {
|
public void setItems(Fields items) {
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
adapter.replaceItems(items);
|
adapter.replaceItems(items);
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/CardViewStyle">
|
style="@style/CardViewStyle">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/item_field_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="@dimen/item_field_card_padding">
|
android:padding="@dimen/item_field_card_padding">
|
||||||
|
@ -18,7 +20,8 @@
|
||||||
android:layout_margin="@dimen/item_field_layout_margin"
|
android:layout_margin="@dimen/item_field_layout_margin"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@id/item_field_key"
|
app:layout_constraintTop_toTopOf="@id/item_field_key"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/item_field_key" />
|
app:layout_constraintBottom_toBottomOf="@id/item_field_key"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/item_field_key"
|
android:id="@+id/item_field_key"
|
||||||
|
@ -38,6 +41,7 @@
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:layout_margin="@dimen/item_field_layout_margin"
|
android:layout_margin="@dimen/item_field_layout_margin"
|
||||||
android:textSize="@dimen/item_field_textsize_value"
|
android:textSize="@dimen/item_field_textsize_value"
|
||||||
|
android:linksClickable="true"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/item_field_key"
|
app:layout_constraintTop_toBottomOf="@id/item_field_key"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
|
@ -69,15 +69,14 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/item_message_screenname"
|
android:id="@+id/item_message_screenname"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/dmitem_text_margin"
|
android:layout_marginStart="@dimen/dmitem_text_margin"
|
||||||
android:layout_marginEnd="@dimen/dmitem_text_margin"
|
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textSize="@dimen/dmitem_textsize_name"
|
android:textSize="@dimen/dmitem_textsize_name"
|
||||||
app:layout_constraintStart_toEndOf="@id/item_message_private"
|
app:layout_constraintStart_toEndOf="@id/item_message_private"
|
||||||
app:layout_constraintTop_toBottomOf="@id/item_message_username"
|
app:layout_constraintTop_toBottomOf="@id/item_message_username"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/item_message_profile"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed" />
|
app:layout_constraintHorizontal_chainStyle="packed" />
|
||||||
|
|
||||||
|
@ -104,6 +103,7 @@
|
||||||
android:id="@+id/item_message_attachment_list"
|
android:id="@+id/item_message_attachment_list"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/dmitem_indicator_size"
|
android:layout_height="@dimen/dmitem_indicator_size"
|
||||||
|
android:layout_marginTop="@dimen/dmitem_text_margin"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/item_message_text"
|
app:layout_constraintTop_toBottomOf="@id/item_message_text"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
|
@ -49,15 +49,22 @@
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
<integer-array name="profile_tab_icons">
|
<integer-array name="profile_tab_icons">
|
||||||
|
<item>@drawable/home</item>
|
||||||
|
<item>@drawable/info</item>
|
||||||
|
</integer-array>
|
||||||
|
|
||||||
|
<integer-array name="profile_tab_icons_favorite">
|
||||||
<item>@drawable/home</item>
|
<item>@drawable/home</item>
|
||||||
<item>@drawable/favorite</item>
|
<item>@drawable/favorite</item>
|
||||||
<item>@drawable/bookmark</item>
|
<item>@drawable/bookmark</item>
|
||||||
|
<item>@drawable/info</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
<integer-array name="profile_tab_icons_like">
|
<integer-array name="profile_tab_icons_like">
|
||||||
<item>@drawable/home</item>
|
<item>@drawable/home</item>
|
||||||
<item>@drawable/like</item>
|
<item>@drawable/like</item>
|
||||||
<item>@drawable/bookmark</item>
|
<item>@drawable/bookmark</item>
|
||||||
|
<item>@drawable/info</item>
|
||||||
</integer-array>
|
</integer-array>
|
||||||
|
|
||||||
<integer-array name="userlist_tab_icons">
|
<integer-array name="userlist_tab_icons">
|
||||||
|
|
|
@ -321,7 +321,7 @@
|
||||||
<string name="status_translate_text">translate</string>
|
<string name="status_translate_text">translate</string>
|
||||||
<string name="status_translate_source">Translated by:\u0020</string>
|
<string name="status_translate_source">Translated by:\u0020</string>
|
||||||
<string name="status_translate_source_language">Language:\u0020</string>
|
<string name="status_translate_source_language">Language:\u0020</string>
|
||||||
<string name="field_verified_in">verified in\u0020</string>
|
<string name="field_verified">verified:\u0020</string>
|
||||||
<string name="confirm_remove_account">remove account from list?</string>
|
<string name="confirm_remove_account">remove account from list?</string>
|
||||||
<string name="confirm_remove_filter">delete filter?</string>
|
<string name="confirm_remove_filter">delete filter?</string>
|
||||||
<string name="account_user_unnamed">\'unnamed\'</string>
|
<string name="account_user_unnamed">\'unnamed\'</string>
|
||||||
|
|
Loading…
Reference in New Issue