Compare commits
41 Commits
1.2.0+fork
...
feature/lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8cd151c7e | ||
|
|
1ada4c9f46 | ||
|
|
b5844a5f8c | ||
|
|
6f37eb9625 | ||
|
|
6404d9cf9a | ||
|
|
1837e5204b | ||
|
|
9ae0f49bff | ||
|
|
269794bfc3 | ||
|
|
a445baf3f0 | ||
|
|
08a3ec99ce | ||
|
|
68f88c29d3 | ||
|
|
9e9ff07eaf | ||
|
|
f377dca63c | ||
|
|
3074100432 | ||
|
|
ce67fb30bf | ||
|
|
da5f3a9094 | ||
|
|
e12d19d0d6 | ||
|
|
f6a35e92c7 | ||
|
|
1e2dd3dec6 | ||
|
|
7ea42c8403 | ||
|
|
af9b527f35 | ||
|
|
df58cdd86e | ||
|
|
507fcea646 | ||
|
|
5cd1e88da9 | ||
|
|
e2293899f0 | ||
|
|
1c743ee3a6 | ||
|
|
daf4c69df4 | ||
|
|
14d3add7b3 | ||
|
|
7e3193a708 | ||
|
|
545aa16cd3 | ||
|
|
4dcf32d13a | ||
|
|
e0aba23e80 | ||
|
|
b19ae9bb10 | ||
|
|
d20f8669e8 | ||
|
|
1567e5aba4 | ||
|
|
6b9b6710cf | ||
|
|
b07858a66d | ||
|
|
c05d0b600e | ||
|
|
dd582c4bee | ||
|
|
3a0d314af0 | ||
|
|
a00ca599c1 |
@@ -46,6 +46,7 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean bottomEncoding;
|
public static boolean bottomEncoding;
|
||||||
public static boolean collapseLongPosts;
|
public static boolean collapseLongPosts;
|
||||||
public static boolean spectatorMode;
|
public static boolean spectatorMode;
|
||||||
|
public static boolean autoHideFab;
|
||||||
public static String publishButtonText;
|
public static String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
@@ -99,6 +100,7 @@ public class GlobalUserPreferences{
|
|||||||
bottomEncoding=prefs.getBoolean("bottomEncoding", false);
|
bottomEncoding=prefs.getBoolean("bottomEncoding", false);
|
||||||
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
|
collapseLongPosts=prefs.getBoolean("collapseLongPosts", true);
|
||||||
spectatorMode=prefs.getBoolean("spectatorMode", false);
|
spectatorMode=prefs.getBoolean("spectatorMode", false);
|
||||||
|
autoHideFab=prefs.getBoolean("autoHideFab", true);
|
||||||
publishButtonText=prefs.getString("publishButtonText", "");
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
@@ -147,6 +149,7 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
.putBoolean("prefixRepliesWithRe", prefixRepliesWithRe)
|
||||||
.putBoolean("collapseLongPosts", collapseLongPosts)
|
.putBoolean("collapseLongPosts", collapseLongPosts)
|
||||||
.putBoolean("spectatorMode", spectatorMode)
|
.putBoolean("spectatorMode", spectatorMode)
|
||||||
|
.putBoolean("autoHideFab", autoHideFab)
|
||||||
.putString("publishButtonText", publishButtonText)
|
.putString("publishButtonText", publishButtonText)
|
||||||
.putBoolean("bottomEncoding", bottomEncoding)
|
.putBoolean("bottomEncoding", bottomEncoding)
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
|
import org.joinmastodon.android.model.Filter;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
|
public class CustomLocalTimelineFragment extends PinnableStatusListFragment {
|
||||||
|
// private String name;
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
private String maxID;
|
||||||
|
@Override
|
||||||
|
protected boolean withComposeButton() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Activity activity){
|
||||||
|
super.onAttach(activity);
|
||||||
|
domain=getArguments().getString("domain");
|
||||||
|
updateTitle(domain);
|
||||||
|
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTitle(String domain) {
|
||||||
|
this.domain = domain;
|
||||||
|
setTitle(this.domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TimelineDefinition makeTimelineDefinition() {
|
||||||
|
return TimelineDefinition.ofCustomLocalTimeline(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count)
|
||||||
|
.setCallback(new SimpleCallback<>(this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<Status> result){
|
||||||
|
if(!result.isEmpty())
|
||||||
|
maxID=result.get(result.size()-1).id;
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList());
|
||||||
|
onDataLoaded(result, !result.isEmpty());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.execNoAuth(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
|
super.onShown();
|
||||||
|
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import android.view.SubMenu;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
@@ -30,6 +31,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.lists.GetLists;
|
import org.joinmastodon.android.api.requests.lists.GetLists;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
import org.joinmastodon.android.api.requests.tags.GetFollowedHashtags;
|
||||||
|
import org.joinmastodon.android.model.CustomLocalTimeline;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.ListTimeline;
|
import org.joinmastodon.android.model.ListTimeline;
|
||||||
@@ -49,6 +51,7 @@ import me.grishka.appkit.api.Callback;
|
|||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefinition> implements ScrollableToTop {
|
||||||
@@ -60,6 +63,7 @@ public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefiniti
|
|||||||
private final Map<MenuItem, TimelineDefinition> timelineByMenuItem = new HashMap<>();
|
private final Map<MenuItem, TimelineDefinition> timelineByMenuItem = new HashMap<>();
|
||||||
private final List<ListTimeline> listTimelines = new ArrayList<>();
|
private final List<ListTimeline> listTimelines = new ArrayList<>();
|
||||||
private final List<Hashtag> hashtags = new ArrayList<>();
|
private final List<Hashtag> hashtags = new ArrayList<>();
|
||||||
|
private final List<CustomLocalTimeline> localTimelines = new ArrayList<>();
|
||||||
|
|
||||||
public EditTimelinesFragment() {
|
public EditTimelinesFragment() {
|
||||||
super(10);
|
super(10);
|
||||||
@@ -128,6 +132,10 @@ public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefiniti
|
|||||||
optionsMenu.performIdentifierAction(R.id.menu_add_timeline, 0);
|
optionsMenu.performIdentifierAction(R.id.menu_add_timeline, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (item.getItemId() == R.id.menu_add_local_timelines) {
|
||||||
|
addNewLocalTimeline();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
TimelineDefinition tl = timelineByMenuItem.get(item);
|
TimelineDefinition tl = timelineByMenuItem.get(item);
|
||||||
if (tl != null) {
|
if (tl != null) {
|
||||||
data.add(tl.copy());
|
data.add(tl.copy());
|
||||||
@@ -138,6 +146,26 @@ public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefiniti
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addNewLocalTimeline() {
|
||||||
|
FrameLayout inputWrap = new FrameLayout(getContext());
|
||||||
|
EditText input = new EditText(getContext());
|
||||||
|
input.setHint(R.string.sk_example_domain);
|
||||||
|
input.setText(GlobalUserPreferences.publishButtonText.trim());
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.setMargins(V.dp(16), V.dp(4), V.dp(16), V.dp(16));
|
||||||
|
input.setLayoutParams(params);
|
||||||
|
inputWrap.addView(input);
|
||||||
|
new M3AlertDialogBuilder(getContext()).setTitle(R.string.mo_add_custom_server_local_timeline).setView(inputWrap)
|
||||||
|
.setPositiveButton(R.string.save, (d, which) -> {
|
||||||
|
TimelineDefinition tl = TimelineDefinition.ofCustomLocalTimeline(input.getText().toString().trim());
|
||||||
|
data.add(tl);
|
||||||
|
saveTimelines();
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, (d, which) -> {
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void addTimelineToOptions(TimelineDefinition tl, Menu menu) {
|
private void addTimelineToOptions(TimelineDefinition tl, Menu menu) {
|
||||||
if (data.contains(tl)) return;
|
if (data.contains(tl)) return;
|
||||||
MenuItem item = menu.add(0, View.generateViewId(), Menu.NONE, tl.getTitle(getContext()));
|
MenuItem item = menu.add(0, View.generateViewId(), Menu.NONE, tl.getTitle(getContext()));
|
||||||
@@ -161,6 +189,9 @@ public class EditTimelinesFragment extends BaseRecyclerFragment<TimelineDefiniti
|
|||||||
SubMenu hashtagsMenu = menu.addSubMenu(R.string.sk_hashtag);
|
SubMenu hashtagsMenu = menu.addSubMenu(R.string.sk_hashtag);
|
||||||
hashtagsMenu.getItem().setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
hashtagsMenu.getItem().setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
||||||
|
|
||||||
|
MenuItem addLocalTimelines = menu.add(0, R.id.menu_add_local_timelines, NONE, R.string.local_timeline);
|
||||||
|
addLocalTimelines.setIcon(R.drawable.ic_fluent_people_community_24_regular);
|
||||||
|
|
||||||
makeBackItem(timelinesMenu);
|
makeBackItem(timelinesMenu);
|
||||||
makeBackItem(listsMenu);
|
makeBackItem(listsMenu);
|
||||||
makeBackItem(hashtagsMenu);
|
makeBackItem(hashtagsMenu);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.SubMenu;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewOutlineProvider;
|
import android.view.ViewOutlineProvider;
|
||||||
@@ -361,6 +362,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
});
|
});
|
||||||
|
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
|
actionButton.setOnLongClickListener(this::onActionButtonLongClick);
|
||||||
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
notifyButton.setOnClickListener(this::onNotifyButtonClick);
|
||||||
avatar.setOnClickListener(this::onAvatarClick);
|
avatar.setOnClickListener(this::onAvatarClick);
|
||||||
cover.setOnClickListener(this::onCoverClick);
|
cover.setOnClickListener(this::onCoverClick);
|
||||||
@@ -576,8 +578,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
for (Account.Role role : account.roles) {
|
for (Account.Role role : account.roles) {
|
||||||
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
TextView roleText = new TextView(getActivity(), null, 0, R.style.role_label);
|
||||||
roleText.setText(role.name);
|
roleText.setText(role.name);
|
||||||
|
if (!TextUtils.isEmpty(role.color) && role.color.startsWith("#")) try {
|
||||||
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
GradientDrawable bg = (GradientDrawable) roleText.getBackground().mutate();
|
||||||
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
bg.setStroke(V.dp(2), Color.parseColor(role.color));
|
||||||
|
} catch (Exception ignored) {}
|
||||||
rolesView.addView(roleText);
|
rolesView.addView(roleText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -706,6 +710,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}else{
|
}else{
|
||||||
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags, R.id.favorites, R.id.scheduled);
|
||||||
}
|
}
|
||||||
|
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
||||||
|
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
|
||||||
|
openWithAccounts.setVisible(hasMultipleAccounts);
|
||||||
|
SubMenu accountsMenu = openWithAccounts.getSubMenu();
|
||||||
|
if (hasMultipleAccounts) {
|
||||||
|
accountsMenu.clear();
|
||||||
|
UiUtils.populateAccountsMenu(accountID, accountsMenu, s-> UiUtils.openURL(
|
||||||
|
getActivity(), s.getID(), account.url, false
|
||||||
|
));
|
||||||
|
}
|
||||||
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
|
||||||
if(isOwnProfile)
|
if(isOwnProfile)
|
||||||
return;
|
return;
|
||||||
@@ -880,6 +894,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(GlobalUserPreferences.enableFabAutoHide){
|
||||||
int dy = scrollY - oldScrollY;
|
int dy = scrollY - oldScrollY;
|
||||||
|
|
||||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||||
@@ -889,7 +904,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
0,
|
0,
|
||||||
fab.getHeight() * 2);
|
fab.getHeight() * 2);
|
||||||
animate.setDuration(300);
|
animate.setDuration(300);
|
||||||
animate.setFillAfter(true);
|
|
||||||
fab.startAnimation(animate);
|
fab.startAnimation(animate);
|
||||||
fab.setVisibility(View.INVISIBLE);
|
fab.setVisibility(View.INVISIBLE);
|
||||||
scrollDiff = 0;
|
scrollDiff = 0;
|
||||||
@@ -902,7 +916,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
fab.getHeight() * 2,
|
fab.getHeight() * 2,
|
||||||
0);
|
0);
|
||||||
animate.setDuration(300);
|
animate.setDuration(300);
|
||||||
animate.setFillAfter(true);
|
|
||||||
fab.startAnimation(animate);
|
fab.startAnimation(animate);
|
||||||
scrollDiff = 0;
|
scrollDiff = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -910,6 +923,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
return switch(page){
|
return switch(page){
|
||||||
@@ -937,6 +951,31 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean onActionButtonLongClick(View v) {
|
||||||
|
if (isOwnProfile || AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
|
||||||
|
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
||||||
|
UiUtils.lookupAccount(getActivity(), account, session.getID(), accountID, acc -> {
|
||||||
|
if (acc == null) return;
|
||||||
|
new SetAccountFollowed(acc.id, true, true).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Relationship relationship) {
|
||||||
|
Toast.makeText(
|
||||||
|
getActivity(),
|
||||||
|
getString(R.string.sk_followed_as, session.self.getShortUsername()),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(session.getID());
|
||||||
|
});
|
||||||
|
}, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void setActionProgressVisible(boolean visible){
|
private void setActionProgressVisible(boolean visible){
|
||||||
actionButton.setTextVisible(!visible);
|
actionButton.setTextVisible(!visible);
|
||||||
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
actionProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
* Represents a user of Mastodon and their associated profile.
|
* Represents a user of Mastodon and their associated profile.
|
||||||
*/
|
*/
|
||||||
@Parcel
|
@Parcel
|
||||||
public class Account extends BaseModel{
|
public class Account extends BaseModel implements Searchable{
|
||||||
// Base attributes
|
// Base attributes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,6 +135,11 @@ public class Account extends BaseModel{
|
|||||||
|
|
||||||
public List<Role> roles;
|
public List<Role> roles;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public static class Role {
|
public static class Role {
|
||||||
public String name;
|
public String name;
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public class CustomLocalTimeline extends BaseModel{
|
||||||
|
@RequiredField
|
||||||
|
public String domain;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return "Hashtag{"+
|
||||||
|
", url='"+domain+'\''+
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,7 +121,7 @@ public class Instance extends BaseModel{
|
|||||||
ci.domain=uri;
|
ci.domain=uri;
|
||||||
ci.normalizedDomain=IDN.toUnicode(uri);
|
ci.normalizedDomain=IDN.toUnicode(uri);
|
||||||
ci.description=Html.fromHtml(shortDescription).toString().trim();
|
ci.description=Html.fromHtml(shortDescription).toString().trim();
|
||||||
if(languages!=null){
|
if(languages!=null && languages.size() > 0){
|
||||||
ci.language=languages.get(0);
|
ci.language=languages.get(0);
|
||||||
ci.languages=languages;
|
ci.languages=languages;
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
public interface Searchable {
|
||||||
|
String getQuery();
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import java.time.Instant;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public class Status extends BaseModel implements DisplayItemsParent{
|
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||||
@RequiredField
|
@RequiredField
|
||||||
public String id;
|
public String id;
|
||||||
@RequiredField
|
@RequiredField
|
||||||
@@ -163,4 +163,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
|||||||
s.filtered = List.of();
|
s.filtered = List.of();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuery() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import androidx.annotation.StringRes;
|
|||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.fragments.CustomLocalTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||||
@@ -27,7 +28,7 @@ public class TimelineDefinition {
|
|||||||
|
|
||||||
private @Nullable String listId;
|
private @Nullable String listId;
|
||||||
private @Nullable String listTitle;
|
private @Nullable String listTitle;
|
||||||
|
private @Nullable String domain;
|
||||||
private @Nullable String hashtagName;
|
private @Nullable String hashtagName;
|
||||||
|
|
||||||
public static TimelineDefinition ofList(String listId, String listTitle) {
|
public static TimelineDefinition ofList(String listId, String listTitle) {
|
||||||
@@ -47,6 +48,12 @@ public class TimelineDefinition {
|
|||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TimelineDefinition ofCustomLocalTimeline(String domain) {
|
||||||
|
TimelineDefinition def = new TimelineDefinition(TimelineType.CUSTOM_LOCAL_TIMELINE);
|
||||||
|
def.domain = domain;
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
public static TimelineDefinition ofHashtag(Hashtag hashtag) {
|
public static TimelineDefinition ofHashtag(Hashtag hashtag) {
|
||||||
return ofHashtag(hashtag.name);
|
return ofHashtag(hashtag.name);
|
||||||
}
|
}
|
||||||
@@ -78,6 +85,7 @@ public class TimelineDefinition {
|
|||||||
case POST_NOTIFICATIONS -> ctx.getString(R.string.sk_timeline_posts);
|
case POST_NOTIFICATIONS -> ctx.getString(R.string.sk_timeline_posts);
|
||||||
case LIST -> listTitle;
|
case LIST -> listTitle;
|
||||||
case HASHTAG -> hashtagName;
|
case HASHTAG -> hashtagName;
|
||||||
|
case CUSTOM_LOCAL_TIMELINE -> domain;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +97,7 @@ public class TimelineDefinition {
|
|||||||
case POST_NOTIFICATIONS -> Icon.POST_NOTIFICATIONS;
|
case POST_NOTIFICATIONS -> Icon.POST_NOTIFICATIONS;
|
||||||
case LIST -> Icon.LIST;
|
case LIST -> Icon.LIST;
|
||||||
case HASHTAG -> Icon.HASHTAG;
|
case HASHTAG -> Icon.HASHTAG;
|
||||||
|
case CUSTOM_LOCAL_TIMELINE -> Icon.CUSTOM_LOCAL_TIMELINE;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +109,7 @@ public class TimelineDefinition {
|
|||||||
case LIST -> new ListTimelineFragment();
|
case LIST -> new ListTimelineFragment();
|
||||||
case HASHTAG -> new HashtagTimelineFragment();
|
case HASHTAG -> new HashtagTimelineFragment();
|
||||||
case POST_NOTIFICATIONS -> new NotificationsListFragment();
|
case POST_NOTIFICATIONS -> new NotificationsListFragment();
|
||||||
|
case CUSTOM_LOCAL_TIMELINE -> new CustomLocalTimelineFragment();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +135,7 @@ public class TimelineDefinition {
|
|||||||
if (type != that.type) return false;
|
if (type != that.type) return false;
|
||||||
if (type == TimelineType.LIST) return Objects.equals(listId, that.listId);
|
if (type == TimelineType.LIST) return Objects.equals(listId, that.listId);
|
||||||
if (type == TimelineType.HASHTAG) return Objects.equals(hashtagName.toLowerCase(), that.hashtagName.toLowerCase());
|
if (type == TimelineType.HASHTAG) return Objects.equals(hashtagName.toLowerCase(), that.hashtagName.toLowerCase());
|
||||||
|
if (type == TimelineType.CUSTOM_LOCAL_TIMELINE) return Objects.equals(domain.toLowerCase(), that.domain.toLowerCase());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +144,8 @@ public class TimelineDefinition {
|
|||||||
int result = type.ordinal();
|
int result = type.ordinal();
|
||||||
result = 31 * result + (listId != null ? listId.hashCode() : 0);
|
result = 31 * result + (listId != null ? listId.hashCode() : 0);
|
||||||
result = 31 * result + (hashtagName.toLowerCase() != null ? hashtagName.toLowerCase().hashCode() : 0);
|
result = 31 * result + (hashtagName.toLowerCase() != null ? hashtagName.toLowerCase().hashCode() : 0);
|
||||||
|
result = 31 * result + (domain.toLowerCase() != null ? domain.toLowerCase().hashCode() : 0);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +155,7 @@ public class TimelineDefinition {
|
|||||||
def.listId = listId;
|
def.listId = listId;
|
||||||
def.listTitle = listTitle;
|
def.listTitle = listTitle;
|
||||||
def.hashtagName = hashtagName;
|
def.hashtagName = hashtagName;
|
||||||
|
def.domain = domain;
|
||||||
def.icon = icon == null ? null : Icon.values()[icon.ordinal()];
|
def.icon = icon == null ? null : Icon.values()[icon.ordinal()];
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
@@ -152,11 +166,13 @@ public class TimelineDefinition {
|
|||||||
args.putString("listID", listId);
|
args.putString("listID", listId);
|
||||||
} else if (type == TimelineType.HASHTAG) {
|
} else if (type == TimelineType.HASHTAG) {
|
||||||
args.putString("hashtag", hashtagName);
|
args.putString("hashtag", hashtagName);
|
||||||
|
} else if (type == TimelineType.CUSTOM_LOCAL_TIMELINE) {
|
||||||
|
args.putString("domain", domain);
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TimelineType { HOME, LOCAL, FEDERATED, POST_NOTIFICATIONS, LIST, HASHTAG }
|
public enum TimelineType { HOME, LOCAL, FEDERATED, POST_NOTIFICATIONS, LIST, HASHTAG, CUSTOM_LOCAL_TIMELINE }
|
||||||
|
|
||||||
public enum Icon {
|
public enum Icon {
|
||||||
HEART(R.drawable.ic_fluent_heart_24_regular, R.string.sk_icon_heart),
|
HEART(R.drawable.ic_fluent_heart_24_regular, R.string.sk_icon_heart),
|
||||||
@@ -219,7 +235,8 @@ public class TimelineDefinition {
|
|||||||
FEDERATED(R.drawable.ic_fluent_earth_24_regular, R.string.sk_timeline_federated, true),
|
FEDERATED(R.drawable.ic_fluent_earth_24_regular, R.string.sk_timeline_federated, true),
|
||||||
POST_NOTIFICATIONS(R.drawable.ic_fluent_chat_24_regular, R.string.sk_timeline_posts, true),
|
POST_NOTIFICATIONS(R.drawable.ic_fluent_chat_24_regular, R.string.sk_timeline_posts, true),
|
||||||
LIST(R.drawable.ic_fluent_people_24_regular, R.string.sk_list, true),
|
LIST(R.drawable.ic_fluent_people_24_regular, R.string.sk_list, true),
|
||||||
HASHTAG(R.drawable.ic_fluent_number_symbol_24_regular, R.string.sk_hashtag, true);
|
HASHTAG(R.drawable.ic_fluent_number_symbol_24_regular, R.string.sk_hashtag, true),
|
||||||
|
CUSTOM_LOCAL_TIMELINE(R.drawable.ic_fluent_people_community_24_regular, R.string.sk_timeline_local, true);
|
||||||
|
|
||||||
public final int iconRes, nameRes;
|
public final int iconRes, nameRes;
|
||||||
public final boolean hidden;
|
public final boolean hidden;
|
||||||
|
|||||||
@@ -205,6 +205,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
String accountID = session.getID();
|
String accountID = session.getID();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
|
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
|
||||||
|
if (status == null) return;
|
||||||
args.putParcelable("replyTo", Parcels.wrap(status));
|
args.putParcelable("replyTo", Parcels.wrap(status));
|
||||||
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||||
});
|
});
|
||||||
@@ -312,11 +313,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
favorite.setSelected(!item.status.favourited);
|
favorite.setSelected(!item.status.favourited);
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{
|
||||||
if (item.status.favourited) {
|
if (item.status.favourited) {
|
||||||
// if(GlobalUserPreferences.reduceMotion){
|
v.startAnimation(GlobalUserPreferences.reduceMotion ? opacityIn : animSet);
|
||||||
v.startAnimation(opacityIn);
|
|
||||||
// }else{
|
|
||||||
// v.startAnimation(animSet);
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
v.startAnimation(opacityIn);
|
v.startAnimation(opacityIn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -481,7 +481,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if (hasMultipleAccounts && accountsMenu != null) {
|
if (hasMultipleAccounts && accountsMenu != null) {
|
||||||
openWithAccounts.setVisible(true);
|
openWithAccounts.setVisible(true);
|
||||||
accountsMenu.clear();
|
accountsMenu.clear();
|
||||||
populateAccountsMenu(accountsMenu);
|
UiUtils.populateAccountsMenu(item.accountID, accountsMenu, s-> UiUtils.openURL(
|
||||||
|
item.parentFragment.getActivity(), s.getID(), item.status.url, false
|
||||||
|
));
|
||||||
} else if (openWithAccounts != null) {
|
} else if (openWithAccounts != null) {
|
||||||
openWithAccounts.setVisible(false);
|
openWithAccounts.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ import org.joinmastodon.android.model.Notification;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.SearchResults;
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
|
import org.joinmastodon.android.model.Searchable;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
@@ -107,8 +108,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -978,6 +981,8 @@ public class UiUtils{
|
|||||||
public static void pickInteractAs(Context context, String accountID, Status sourceStatus, Predicate<Status> checkInteracted, InteractionPerformer interactionPerformer, @StringRes int interactAsRes, @StringRes int interactedAsAccountRes, @StringRes int alreadyInteractedRes, @DrawableRes int iconRes) {
|
public static void pickInteractAs(Context context, String accountID, Status sourceStatus, Predicate<Status> checkInteracted, InteractionPerformer interactionPerformer, @StringRes int interactAsRes, @StringRes int interactedAsAccountRes, @StringRes int alreadyInteractedRes, @DrawableRes int iconRes) {
|
||||||
pickAccount(context, accountID, interactAsRes, iconRes, session -> {
|
pickAccount(context, accountID, interactAsRes, iconRes, session -> {
|
||||||
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
|
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
|
||||||
|
if (status == null) return;
|
||||||
|
|
||||||
if (checkInteracted.test(status)) {
|
if (checkInteracted.test(status)) {
|
||||||
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
@@ -993,17 +998,33 @@ public class UiUtils{
|
|||||||
}, null);
|
}, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> statusConsumer) {
|
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> resultConsumer) {
|
||||||
|
lookup(context, queryStatus, targetAccountID, sourceAccountID, GetSearchResults.Type.STATUSES, resultConsumer, results ->
|
||||||
|
!results.statuses.isEmpty() ? Optional.of(results.statuses.get(0)) : Optional.empty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void lookupAccount(Context context, Account queryAccount, String targetAccountID, @Nullable String sourceAccountID, Consumer<Account> resultConsumer) {
|
||||||
|
lookup(context, queryAccount, targetAccountID, sourceAccountID, GetSearchResults.Type.ACCOUNTS, resultConsumer, results ->
|
||||||
|
!results.accounts.isEmpty() ? Optional.of(results.accounts.get(0)) : Optional.empty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Searchable> void lookup(Context context, T query, String targetAccountID, @Nullable String sourceAccountID, @Nullable GetSearchResults.Type type, Consumer<T> resultConsumer, Function<SearchResults, Optional<T>> extractResult) {
|
||||||
if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) {
|
if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) {
|
||||||
statusConsumer.accept(queryStatus);
|
resultConsumer.accept(query);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new GetSearchResults(queryStatus.url, GetSearchResults.Type.STATUSES, true).setCallback(new Callback<>() {
|
new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults results) {
|
public void onSuccess(SearchResults results) {
|
||||||
if (!results.statuses.isEmpty()) statusConsumer.accept(results.statuses.get(0));
|
Optional<T> result = extractResult.apply(results);
|
||||||
else Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
if (result.isPresent()) resultConsumer.accept(result.get());
|
||||||
|
else {
|
||||||
|
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||||
|
resultConsumer.accept(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1239,6 +1260,17 @@ public class UiUtils{
|
|||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void populateAccountsMenu(String excludeAccountID, Menu menu, Consumer<AccountSession> onClick) {
|
||||||
|
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
||||||
|
sessions.stream().filter(s -> !s.getID().equals(excludeAccountID)).forEach(s -> {
|
||||||
|
String username = "@"+s.self.username+"@"+s.domain;
|
||||||
|
menu.add(username).setOnMenuItemClickListener((c) -> {
|
||||||
|
onClick.accept(s);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a View.OnClickListener to filter multiple clicks in succession.
|
* Wraps a View.OnClickListener to filter multiple clicks in succession.
|
||||||
* Useful for buttons that perform some action that changes their state asynchronously.
|
* Useful for buttons that perform some action that changes their state asynchronously.
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<corners android:radius="4sp" />
|
<corners android:radius="4sp" />
|
||||||
<solid android:color="?colorBackgroundLight" />
|
<solid android:color="?colorBackgroundLight" />
|
||||||
<stroke android:width="2dp" android:color="#00ff00" />
|
<stroke android:width="2dp" android:color="?android:colorAccent" />
|
||||||
</shape>
|
</shape>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
|
||||||
|
<path android:pathData="M15.114 25.719c-0.396-0.408-0.746-0.861-1.04-1.35C13.418 24.453 12.725 24.5 12 24.5c-5.111 0-8.5-2.111-8.5-4.785V19l0.007-0.145C3.58 18.095 4.22 17.5 5 17.5h8.624c0.234-0.535 0.529-1.038 0.875-1.5H5c-1.657 0-3 1.343-3 3v0.715C2 23.433 6.21 26 12 26c1.101 0 2.145-0.098 3.114-0.281zM18 8c0-3.314-2.686-6-6-6S6 4.686 6 8s2.686 6 6 6 6-2.686 6-6zM7.5 8c0-2.485 2.015-4.5 4.5-4.5s4.5 2.015 4.5 4.5-2.015 4.5-4.5 4.5S7.5 10.485 7.5 8zm13 19c3.59 0 6.5-2.91 6.5-6.5S24.09 14 20.5 14 14 16.91 14 20.5s2.91 6.5 6.5 6.5zm0-11c0.276 0 0.5 0.224 0.5 0.5V20h3.5c0.276 0 0.5 0.224 0.5 0.5S24.776 21 24.5 21H21v3.5c0 0.276-0.224 0.5-0.5 0.5S20 24.776 20 24.5V21h-3.5c-0.276 0-0.5-0.224-0.5-0.5s0.224-0.5 0.5-0.5H20v-3.5c0-0.276 0.224-0.5 0.5-0.5z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
android:id="@+id/favorite"
|
android:id="@+id/favorite"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center"
|
||||||
android:drawableStart="@drawable/ic_fluent_star_24_selector"
|
android:drawableStart="@drawable/ic_fluent_star_24_selector"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:paddingHorizontal="8dp"
|
android:paddingHorizontal="8dp"
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account" android:icon="@drawable/ic_fluent_person_swap_24_regular">
|
||||||
|
<menu android:id="@+id/accounts" />
|
||||||
|
</item>
|
||||||
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
|
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
|
||||||
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_mute_24_regular"/>
|
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_mute_24_regular"/>
|
||||||
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
|
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:id="@+id/followed_hashtags" android:title="@string/sk_hashtags_you_follow" android:icon="@drawable/ic_fluent_number_symbol_24_regular"/>
|
<item android:id="@+id/followed_hashtags" android:title="@string/sk_hashtags_you_follow" android:icon="@drawable/ic_fluent_number_symbol_24_regular"/>
|
||||||
<item android:id="@+id/bookmarks" android:title="@string/bookmarks" android:icon="@drawable/ic_fluent_bookmark_multiple_24_regular" android:showAsAction="always"/>
|
<item android:id="@+id/bookmarks" android:title="@string/bookmarks" android:icon="@drawable/ic_fluent_bookmark_multiple_24_regular" android:showAsAction="always"/>
|
||||||
|
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account" android:icon="@drawable/ic_fluent_person_swap_24_regular">
|
||||||
|
<menu android:id="@+id/accounts" />
|
||||||
|
</item>
|
||||||
<item android:id="@+id/manage_user_lists" android:title="@string/sk_your_lists" android:icon="@drawable/ic_fluent_people_24_regular"/>
|
<item android:id="@+id/manage_user_lists" android:title="@string/sk_your_lists" android:icon="@drawable/ic_fluent_people_24_regular"/>
|
||||||
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular" android:showAsAction="always"/>
|
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular" android:showAsAction="always"/>
|
||||||
<item android:id="@+id/scheduled" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular" android:showAsAction="always"/>
|
<item android:id="@+id/scheduled" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular" android:showAsAction="always"/>
|
||||||
|
|||||||
@@ -258,4 +258,7 @@
|
|||||||
<string name="sk_unfinished_attachments">Anhänge richtig stellen\?</string>
|
<string name="sk_unfinished_attachments">Anhänge richtig stellen\?</string>
|
||||||
<string name="sk_settings_hide_interaction">Interaktions-Buttons verstecken</string>
|
<string name="sk_settings_hide_interaction">Interaktions-Buttons verstecken</string>
|
||||||
<string name="sk_unfinished_attachments_message">Einige Anhänge sind nicht fertig hochgeladen.</string>
|
<string name="sk_unfinished_attachments_message">Einige Anhänge sind nicht fertig hochgeladen.</string>
|
||||||
|
<string name="sk_followed_as">Mit %s gefolgt</string>
|
||||||
|
<string name="sk_settings_hide_fab">Verfassen-Button automatisch ausblenden</string>
|
||||||
|
<string name="sk_follow_as">Mit anderem Account folgen</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -22,4 +22,5 @@
|
|||||||
|
|
||||||
<item name="menu_add_timeline" type="id" />
|
<item name="menu_add_timeline" type="id" />
|
||||||
<item name="menu_back" type="id" />
|
<item name="menu_back" type="id" />
|
||||||
|
<item name="menu_add_local_timelines" type="id" />
|
||||||
</resources>
|
</resources>
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
<string name="mo_filtered">Filtered: %s</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>
|
||||||
<string name="mo_disable_reminder_to_add_alt_text">Disable reminder to add alt text</string>
|
<string name="mo_disable_reminder_to_add_alt_text">Disable reminder to add alt text</string>
|
||||||
|
<string name="mo_add_custom_server_local_timeline">Add a custom server\'s local timeline</string>
|
||||||
|
|
||||||
<!-- accessibility labels-->
|
<!-- accessibility labels-->
|
||||||
<string name="mo_poll_option_add">Add new poll option</string>
|
<string name="mo_poll_option_add">Add new poll option</string>
|
||||||
|
|||||||
@@ -259,4 +259,7 @@
|
|||||||
<string name="sk_unfinished_attachments">Fix attachments?</string>
|
<string name="sk_unfinished_attachments">Fix attachments?</string>
|
||||||
<string name="sk_unfinished_attachments_message">Some attachments haven’t finished uploading.</string>
|
<string name="sk_unfinished_attachments_message">Some attachments haven’t finished uploading.</string>
|
||||||
<string name="sk_settings_hide_interaction">Hide interaction buttons</string>
|
<string name="sk_settings_hide_interaction">Hide interaction buttons</string>
|
||||||
|
<string name="sk_follow_as">Follow from other account</string>
|
||||||
|
<string name="sk_followed_as">Followed from %s</string>
|
||||||
|
<string name="sk_settings_hide_fab">Auto-hide Compose button</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -6,11 +6,13 @@
|
|||||||
<locale android:name="bs-BA"/>
|
<locale android:name="bs-BA"/>
|
||||||
<locale android:name="ca-ES"/>
|
<locale android:name="ca-ES"/>
|
||||||
<locale android:name="cs-CZ"/>
|
<locale android:name="cs-CZ"/>
|
||||||
|
<locale android:name="da-DK"/>
|
||||||
<locale android:name="de-DE"/>
|
<locale android:name="de-DE"/>
|
||||||
<locale android:name="el-GR"/>
|
<locale android:name="el-GR"/>
|
||||||
<locale android:name="en"/>
|
<locale android:name="en"/>
|
||||||
<locale android:name="es-ES"/>
|
<locale android:name="es-ES"/>
|
||||||
<locale android:name="eu-ES"/>
|
<locale android:name="eu-ES"/>
|
||||||
|
<locale android:name="fa-IR"/>
|
||||||
<locale android:name="fi-FI"/>
|
<locale android:name="fi-FI"/>
|
||||||
<locale android:name="fil-PH"/>
|
<locale android:name="fil-PH"/>
|
||||||
<locale android:name="fr-FR"/>
|
<locale android:name="fr-FR"/>
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
<locale android:name="hr-HR"/>
|
<locale android:name="hr-HR"/>
|
||||||
<locale android:name="hu-HU"/>
|
<locale android:name="hu-HU"/>
|
||||||
<locale android:name="hy-AM"/>
|
<locale android:name="hy-AM"/>
|
||||||
|
<locale android:name="ig-NG"/>
|
||||||
<locale android:name="in-ID"/>
|
<locale android:name="in-ID"/>
|
||||||
<locale android:name="is-IS"/>
|
<locale android:name="is-IS"/>
|
||||||
<locale android:name="it-IT"/>
|
<locale android:name="it-IT"/>
|
||||||
@@ -28,7 +31,9 @@
|
|||||||
<locale android:name="ja-JP"/>
|
<locale android:name="ja-JP"/>
|
||||||
<locale android:name="kab"/>
|
<locale android:name="kab"/>
|
||||||
<locale android:name="ko-KR"/>
|
<locale android:name="ko-KR"/>
|
||||||
|
<locale android:name="my-MM"/>
|
||||||
<locale android:name="nl-NL"/>
|
<locale android:name="nl-NL"/>
|
||||||
|
<locale android:name="no-NO"/>
|
||||||
<locale android:name="oc-FR"/>
|
<locale android:name="oc-FR"/>
|
||||||
<locale android:name="pl-PL"/>
|
<locale android:name="pl-PL"/>
|
||||||
<locale android:name="pt-BR"/>
|
<locale android:name="pt-BR"/>
|
||||||
|
|||||||
4
metadata/de-DE/changelogs/77.txt
Normal file
4
metadata/de-DE/changelogs/77.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- Folgen-Button lange drücken, um Profil von anderem Account zu folgen
|
||||||
|
- Option, um Profile mit anderem Account zu öffnen
|
||||||
|
- Verfassen-Button wird beim Scrollen automatisch ausgeblendet
|
||||||
|
- Crash beim Öffnen von Admin-Accounts behoben
|
||||||
4
metadata/en-US/changelogs/77.txt
Normal file
4
metadata/en-US/changelogs/77.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- Long-press follow button to follow profiles from other account
|
||||||
|
- Option to open profiles in other account
|
||||||
|
- Auto-hide compose button when scrolling down the timeline
|
||||||
|
- Fix crash when opening server admin's profiles
|
||||||
Reference in New Issue
Block a user