Merge remote-tracking branch 'upstream/master' into try-to-merge-upstream
This commit is contained in:
@@ -143,7 +143,7 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
||||
}
|
||||
|
||||
public void openSearchQuery(String q, String accountID, int progressText, boolean fromSearch){
|
||||
new GetSearchResults(q, null, true)
|
||||
new GetSearchResults(q, null, true, null, 0, 0)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
|
||||
@@ -176,6 +176,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
List<NotificationChannel> channels=Arrays.stream(PushNotification.Type.values())
|
||||
.map(type->{
|
||||
NotificationChannel channel=new NotificationChannel(accountID+"_"+type, context.getString(type.localizedName), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.setLightColor(context.getColor(R.color.primary_700));
|
||||
channel.enableLights(true);
|
||||
channel.setGroup(accountID);
|
||||
return channel;
|
||||
})
|
||||
@@ -205,6 +207,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
||||
.setShowWhen(true)
|
||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||
.setAutoCancel(true)
|
||||
.setLights(context.getColor(android.R.attr.colorAccent), 500, 1000)
|
||||
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
||||
|
||||
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
||||
|
||||
@@ -6,18 +6,19 @@ import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
|
||||
public class UpdateAccountCredentialsPreferences extends MastodonAPIRequest<Account>{
|
||||
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable){
|
||||
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable, Boolean indexable){
|
||||
super(HttpMethod.PATCH, "/accounts/update_credentials", Account.class);
|
||||
setRequestBody(new Request(locked, discoverable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
|
||||
setRequestBody(new Request(locked, discoverable, indexable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
|
||||
}
|
||||
|
||||
private static class Request{
|
||||
public Boolean locked, discoverable;
|
||||
public Boolean locked, discoverable, indexable;
|
||||
public RequestSource source;
|
||||
|
||||
public Request(Boolean locked, Boolean discoverable, RequestSource source){
|
||||
public Request(Boolean locked, Boolean discoverable, Boolean indexable, RequestSource source){
|
||||
this.locked=locked;
|
||||
this.discoverable=discoverable;
|
||||
this.indexable=indexable;
|
||||
this.source=source;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import org.joinmastodon.android.model.FilterContext;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
class FilterRequest{
|
||||
public String title;
|
||||
public EnumSet<FilterContext> context;
|
||||
|
||||
@@ -4,13 +4,19 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
|
||||
public class GetSearchResults extends MastodonAPIRequest<SearchResults>{
|
||||
public GetSearchResults(String query, Type type, boolean resolve){
|
||||
public GetSearchResults(String query, Type type, boolean resolve, String maxID, int offset, int count){
|
||||
super(HttpMethod.GET, "/search", SearchResults.class);
|
||||
addQueryParameter("q", query);
|
||||
if(type!=null)
|
||||
addQueryParameter("type", type.name().toLowerCase());
|
||||
if(resolve)
|
||||
addQueryParameter("resolve", "true");
|
||||
if(maxID!=null)
|
||||
addQueryParameter("max_id", maxID);
|
||||
if(offset>0)
|
||||
addQueryParameter("offset", String.valueOf(offset));
|
||||
if(count>0)
|
||||
addQueryParameter("limit", String.valueOf(count));
|
||||
}
|
||||
|
||||
public GetSearchResults limit(int limit){
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.joinmastodon.android.api.requests.tags;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
|
||||
public class GetTag extends MastodonAPIRequest<Hashtag>{
|
||||
public GetTag(String tag){
|
||||
super(HttpMethod.GET, "/tags/"+tag, Hashtag.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.joinmastodon.android.api.requests.tags;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
|
||||
public class SetTagFollowed extends MastodonAPIRequest<Hashtag>{
|
||||
public SetTagFollowed(String tag, boolean followed){
|
||||
super(HttpMethod.POST, "/tags/"+tag+(followed ? "/follow" : "/unfollow"), Hashtag.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
}
|
||||
@@ -219,7 +219,7 @@ public class AccountSession{
|
||||
|
||||
public void savePreferencesIfPending(){
|
||||
if(preferencesNeedSaving){
|
||||
new UpdateAccountCredentialsPreferences(preferences, null, null)
|
||||
new UpdateAccountCredentialsPreferences(preferences, null, self.discoverable, self.source.indexable)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Account result){
|
||||
@@ -303,6 +303,10 @@ public class AccountSession{
|
||||
});
|
||||
}
|
||||
|
||||
public void updateAccountInfo(){
|
||||
AccountSessionManager.getInstance().updateSessionLocalInfo(this);
|
||||
}
|
||||
|
||||
public Optional<Instance> getInstance() {
|
||||
return Optional.ofNullable(AccountSessionManager.getInstance().getInstanceInfo(domain));
|
||||
}
|
||||
|
||||
@@ -303,8 +303,7 @@ public class AccountSessionManager{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateSessionLocalInfo(AccountSession session){
|
||||
/*package*/ void updateSessionLocalInfo(AccountSession session){
|
||||
new GetOwnAccount()
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
|
||||
@@ -1317,7 +1317,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
boolean usePhotoPicker=photoPicker && UiUtils.isPhotoPickerAvailable();
|
||||
if(usePhotoPicker){
|
||||
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount());
|
||||
if(mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount()>1)
|
||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount());
|
||||
}else{
|
||||
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
|
||||
@@ -45,8 +45,8 @@ public class FeaturedHashtagsListFragment extends BaseStatusListFragment<Hashtag
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(String hashtag){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag, data.stream().filter(h -> Objects.equals(h.name, hashtag)).findAny().map(h -> h.following).orElse(null));
|
||||
public void onItemClick(String id){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, Objects.requireNonNull(findItemOfType(id, HashtagStatusDisplayItem.class)).tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -121,7 +121,7 @@ public class FollowedHashtagsFragment extends MastodonRecyclerFragment<Hashtag>
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,64 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||
import org.joinmastodon.android.api.requests.tags.GetTag;
|
||||
import org.joinmastodon.android.api.requests.tags.SetTagFollowed;
|
||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
||||
import org.joinmastodon.android.model.FilterContext;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.model.TimelineDefinition;
|
||||
import org.joinmastodon.android.ui.text.SpacerSpan;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
private String hashtag;
|
||||
public class HashtagTimelineFragment extends PinnableStatusListFragment{
|
||||
private Hashtag hashtag;
|
||||
private String hashtagName;
|
||||
private TextView headerTitle, headerSubtitle;
|
||||
private ProgressBarButton followButton;
|
||||
private ProgressBar followProgress;
|
||||
private MenuItem followMenuItem, pinMenuItem;
|
||||
private boolean followRequestRunning;
|
||||
private boolean toolbarContentVisible;
|
||||
|
||||
private List<String> any;
|
||||
private List<String> all;
|
||||
private List<String> none;
|
||||
private boolean following;
|
||||
private boolean localOnly;
|
||||
private MenuItem followButton;
|
||||
private Menu optionsMenu;
|
||||
private MenuInflater optionsMenuInflater;
|
||||
|
||||
@Override
|
||||
protected boolean wantsComposeButton() {
|
||||
@@ -50,73 +68,19 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
@Override
|
||||
public void onAttach(Activity activity){
|
||||
super.onAttach(activity);
|
||||
updateTitle(getArguments().getString("hashtag"));
|
||||
following=getArguments().getBoolean("following", false);
|
||||
localOnly=getArguments().getBoolean("localOnly", false);
|
||||
any=getArguments().getStringArrayList("any");
|
||||
all=getArguments().getStringArrayList("all");
|
||||
none=getArguments().getStringArrayList("none");
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
private void updateTitle(String hashtagName) {
|
||||
hashtag = hashtagName;
|
||||
setTitle('#'+hashtag);
|
||||
}
|
||||
|
||||
private void updateFollowingState(boolean newFollowing) {
|
||||
this.following = newFollowing;
|
||||
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
||||
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||
E.post(new HashtagUpdatedEvent(hashtag, following));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
followButton = menu.findItem(R.id.follow_hashtag);
|
||||
updateFollowingState(following);
|
||||
|
||||
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Hashtag hashtag) {
|
||||
if (getActivity() == null) return;
|
||||
updateTitle(hashtag.name);
|
||||
updateFollowingState(hashtag.following);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (super.onOptionsItemSelected(item)) return true;
|
||||
if (item.getItemId() == R.id.follow_hashtag) {
|
||||
updateFollowingState(!following);
|
||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Hashtag i) {
|
||||
if (getActivity() == null) return;
|
||||
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
||||
updateFollowingState(i.following);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
error.showToast(getActivity());
|
||||
updateFollowingState(!following);
|
||||
}
|
||||
}).exec(accountID);
|
||||
return true;
|
||||
if(getArguments().containsKey("hashtag")){
|
||||
hashtag=Parcels.unwrap(getArguments().getParcelable("hashtag"));
|
||||
hashtagName=hashtag.name;
|
||||
}else{
|
||||
hashtagName=getArguments().getString("hashtagName");
|
||||
}
|
||||
return false;
|
||||
setTitle('#'+hashtagName);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,12 +90,10 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
|
||||
currentRequest=new GetHashtagTimeline(hashtagName, offset==0 ? null : getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
if (getActivity() == null) return;
|
||||
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||
onDataLoaded(result, !result.isEmpty());
|
||||
}
|
||||
})
|
||||
@@ -146,15 +108,40 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFabLongClick(View v) {
|
||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
||||
public void loadData(){
|
||||
reloadTag();
|
||||
super.loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
fab=view.findViewById(R.id.fab);
|
||||
fab.setOnClickListener(this::onFabClick);
|
||||
|
||||
if(getParentFragment() instanceof HomeTabFragment) return;
|
||||
|
||||
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
||||
View topChild=recyclerView.getChildAt(0);
|
||||
int firstChildPos=recyclerView.getChildAdapterPosition(topChild);
|
||||
float newAlpha=firstChildPos>0 ? 1f : Math.min(1f, -topChild.getTop()/(float)headerTitle.getHeight());
|
||||
toolbarTitleView.setAlpha(newAlpha);
|
||||
boolean newToolbarVisibility=newAlpha>0.5f;
|
||||
if(newToolbarVisibility!=toolbarContentVisible){
|
||||
toolbarContentVisible=newToolbarVisibility;
|
||||
createOptionsMenu();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFabClick(View v){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("prefilledText", '#'+hashtag+' ');
|
||||
args.putString("prefilledText", '#'+hashtagName+' ');
|
||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||
}
|
||||
|
||||
@@ -170,6 +157,203 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||
|
||||
@Override
|
||||
public Uri getWebUri(Uri.Builder base) {
|
||||
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags") + hashtag).build();
|
||||
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags/") + hashtag).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
View header=getActivity().getLayoutInflater().inflate(R.layout.header_hashtag_timeline, list, false);
|
||||
headerTitle=header.findViewById(R.id.title);
|
||||
headerSubtitle=header.findViewById(R.id.subtitle);
|
||||
followButton=header.findViewById(R.id.profile_action_btn);
|
||||
followProgress=header.findViewById(R.id.action_progress);
|
||||
|
||||
headerTitle.setText("#"+hashtagName);
|
||||
followButton.setVisibility(View.GONE);
|
||||
followButton.setOnClickListener(v->{
|
||||
if(hashtag==null)
|
||||
return;
|
||||
setFollowed(!hashtag.following);
|
||||
});
|
||||
followButton.setOnLongClickListener(v->{
|
||||
if(hashtag==null) return false;
|
||||
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
||||
new SetTagFollowed(hashtagName, true).setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Hashtag hashtag) {
|
||||
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;
|
||||
});
|
||||
updateHeader();
|
||||
|
||||
MergeRecyclerAdapter mergeAdapter=new MergeRecyclerAdapter();
|
||||
if(!(getParentFragment() instanceof HomeTabFragment)){
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(header));
|
||||
}
|
||||
mergeAdapter.addAdapter(super.getAdapter());
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMainAdapterOffset(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
private void createOptionsMenu(){
|
||||
optionsMenu.clear();
|
||||
optionsMenuInflater.inflate(R.menu.hashtag_timeline, optionsMenu);
|
||||
followMenuItem=optionsMenu.findItem(R.id.follow_hashtag);
|
||||
pinMenuItem=optionsMenu.findItem(R.id.pin);
|
||||
followMenuItem.setVisible(toolbarContentVisible);
|
||||
followMenuItem.setTitle(getString(hashtag.following ? R.string.unfollow_user : R.string.follow_user, "#"+hashtagName));
|
||||
followMenuItem.setIcon(hashtag.following ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||
pinMenuItem.setShowAsAction(toolbarContentVisible ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
if(toolbarContentVisible){
|
||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu);
|
||||
}else{
|
||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu, R.id.pin);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePinButton(MenuItem pin){
|
||||
super.updatePinButton(pin);
|
||||
UiUtils.insetPopupMenuIcon(getContext(), pin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
optionsMenu=menu;
|
||||
optionsMenuInflater=inflater;
|
||||
createOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item){
|
||||
if (super.onOptionsItemSelected(item)) return true;
|
||||
if (item.getItemId() == R.id.follow_hashtag && hashtag!=null) {
|
||||
setFollowed(!hashtag.following);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
super.onUpdateToolbar();
|
||||
toolbarTitleView.setAlpha(toolbarContentVisible ? 1f : 0f);
|
||||
createOptionsMenu();
|
||||
}
|
||||
|
||||
private void updateHeader(){
|
||||
if(hashtag==null)
|
||||
return;
|
||||
|
||||
if(hashtag.history!=null && !hashtag.history.isEmpty()){
|
||||
int weekPosts=hashtag.history.stream().mapToInt(h->h.uses).sum();
|
||||
int todayPosts=hashtag.history.get(0).uses;
|
||||
int numAccounts=hashtag.history.stream().mapToInt(h->h.accounts).sum();
|
||||
int hSpace=V.dp(8);
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||
ssb.append(getResources().getQuantityString(R.plurals.x_posts, weekPosts, weekPosts));
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append('·');
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append(getResources().getQuantityString(R.plurals.x_participants, numAccounts, numAccounts));
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append('·');
|
||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
||||
ssb.append(getResources().getQuantityString(R.plurals.x_posts_today, todayPosts, todayPosts));
|
||||
headerSubtitle.setText(ssb);
|
||||
}
|
||||
|
||||
int styleRes;
|
||||
followButton.setVisibility(View.VISIBLE);
|
||||
if(hashtag.following){
|
||||
followButton.setText(R.string.button_following);
|
||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
||||
}else{
|
||||
followButton.setText(R.string.button_follow);
|
||||
styleRes=R.style.Widget_Mastodon_M3_Button_Filled;
|
||||
}
|
||||
TypedArray ta=followButton.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
||||
followButton.setBackground(ta.getDrawable(0));
|
||||
ta.recycle();
|
||||
ta=followButton.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.textColor});
|
||||
followButton.setTextColor(ta.getColorStateList(0));
|
||||
followProgress.setIndeterminateTintList(ta.getColorStateList(0));
|
||||
ta.recycle();
|
||||
|
||||
followButton.setTextVisible(true);
|
||||
followProgress.setVisibility(View.GONE);
|
||||
if(followMenuItem!=null){
|
||||
followMenuItem.setTitle(getString(hashtag.following ? R.string.unfollow_user : R.string.follow_user, "#"+hashtagName));
|
||||
followMenuItem.setIcon(hashtag.following ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||
UiUtils.insetPopupMenuIcon(getContext(), followMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadTag(){
|
||||
new GetTag(hashtagName)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Hashtag result){
|
||||
hashtag=result;
|
||||
updateHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
private void setFollowed(boolean followed){
|
||||
if(followRequestRunning)
|
||||
return;
|
||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
followButton.setTextVisible(false);
|
||||
followProgress.setVisibility(View.VISIBLE);
|
||||
followRequestRunning=true;
|
||||
new SetTagFollowed(hashtagName, followed)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Hashtag result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
hashtag=result;
|
||||
updateHeader();
|
||||
followRequestRunning=false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
if(error instanceof MastodonErrorResponse er && "Duplicate record".equals(er.error)){
|
||||
hashtag.following=true;
|
||||
}else{
|
||||
error.showToast(getActivity());
|
||||
}
|
||||
updateHeader();
|
||||
followRequestRunning=false;
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
});
|
||||
}
|
||||
}
|
||||
tabBar.selectTab(currentTab);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -524,9 +524,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
||||
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
||||
args.putString("hashtag", hashtag.name);
|
||||
args.putBoolean("following", hashtag.following);
|
||||
Nav.go(getActivity(), HashtagTimelineFragment.class, args);
|
||||
UiUtils.openHashtagTimeline(getContext(), accountID, hashtag);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -304,6 +304,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurfaceVariant), UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary));
|
||||
tabbar.setTabTextSize(V.dp(14));
|
||||
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, (tab, position)->tab.setText(switch(position){
|
||||
case 0 -> R.string.profile_featured;
|
||||
case 1 -> R.string.profile_timeline;
|
||||
case 2 -> R.string.profile_about;
|
||||
default -> throw new IllegalStateException();
|
||||
}));
|
||||
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
||||
@Override
|
||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||
@@ -317,6 +323,19 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
if (position == 4) tab.view.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
tabbar.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab){}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab){}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab){
|
||||
if(getFragmentForPage(tab.getPosition()) instanceof ScrollableToTop stt)
|
||||
stt.scrollToTop();
|
||||
}
|
||||
});
|
||||
|
||||
cover.setOutlineProvider(new ViewOutlineProvider(){
|
||||
@Override
|
||||
@@ -1304,9 +1323,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
FrameLayout view=new FrameLayout(parent.getContext());
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
@@ -1314,8 +1331,13 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||
Fragment fragment=getFragmentForPage(position);
|
||||
FrameLayout fragmentView=tabViews[position];
|
||||
fragmentView.setVisibility(View.VISIBLE);
|
||||
if(fragmentView.getParent() instanceof ViewGroup parent)
|
||||
parent.removeView(fragmentView);
|
||||
((FrameLayout)holder.itemView).addView(fragmentView);
|
||||
if(!fragment.isAdded()){
|
||||
getChildFragmentManager().beginTransaction().add(holder.itemView.getId(), fragment).commit();
|
||||
getChildFragmentManager().beginTransaction().add(fragmentView.getId(), fragment).commit();
|
||||
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||
@Override
|
||||
public boolean onPreDraw(){
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.SpoilerStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
|
||||
@@ -105,6 +106,12 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
|
||||
text.textSelectable=true;
|
||||
else if(item instanceof FooterStatusDisplayItem footer)
|
||||
footer.hideCounts=true;
|
||||
else if(item instanceof SpoilerStatusDisplayItem spoiler){
|
||||
for(StatusDisplayItem subItem:spoiler.contentItems){
|
||||
if(subItem instanceof TextStatusDisplayItem text)
|
||||
text.textSelectable=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ComposeAccountSearchFragment extends BaseAccountListFragment{
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
refreshing=true;
|
||||
currentRequest=new GetSearchResults(currentQuery, GetSearchResults.Type.ACCOUNTS, false)
|
||||
currentRequest=new GetSearchResults(currentQuery, GetSearchResults.Type.ACCOUNTS, false, null, 0, 0)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
|
||||
@@ -289,15 +289,19 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
FrameLayout view=new FrameLayout(parent.getContext());
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){}
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||
FrameLayout view=tabViews[position];
|
||||
if(view.getParent() instanceof ViewGroup parent)
|
||||
parent.removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
((FrameLayout)holder.itemView).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
@@ -31,7 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.Nav;
|
||||
@@ -97,7 +96,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
args.putParcelable("profileAccount", Parcels.wrap(res.account));
|
||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||
}
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag);
|
||||
case STATUS -> {
|
||||
Status status=res.status.getContentStatus();
|
||||
Bundle args=new Bundle();
|
||||
@@ -113,7 +112,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
protected void doLoadData(int _offset, int count){
|
||||
GetSearchResults.Type type;
|
||||
if(currentFilter.size()==1){
|
||||
type=switch(currentFilter.iterator().next()){
|
||||
@@ -128,7 +127,21 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
dataLoaded();
|
||||
return;
|
||||
}
|
||||
currentRequest=new GetSearchResults(currentQuery, type, true)
|
||||
String maxID=null;
|
||||
// TODO server-side bug
|
||||
/*int offset=0;
|
||||
if(_offset>0){
|
||||
if(type==GetSearchResults.Type.STATUSES){
|
||||
if(!preloadedData.isEmpty())
|
||||
maxID=preloadedData.get(preloadedData.size()-1).status.id;
|
||||
else if(!data.isEmpty())
|
||||
maxID=data.get(data.size()-1).status.id;
|
||||
}else{
|
||||
offset=_offset;
|
||||
}
|
||||
}*/
|
||||
int offset=_offset;
|
||||
currentRequest=new GetSearchResults(currentQuery, type, type==null, maxID, offset, type==null ? 0 : count)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
@@ -142,12 +155,15 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
||||
results.add(new SearchResult(tag));
|
||||
}
|
||||
if(result.statuses!=null){
|
||||
for(Status status:result.statuses)
|
||||
results.add(new SearchResult(status));
|
||||
Set<String> alreadyLoadedStatuses=data.stream().filter(r->r.type==SearchResult.Type.STATUS).map(r->r.status.id).collect(Collectors.toSet());
|
||||
for(Status status:result.statuses){
|
||||
if(!alreadyLoadedStatuses.contains(status.id))
|
||||
results.add(new SearchResult(status));
|
||||
}
|
||||
}
|
||||
prevDisplayItems=new ArrayList<>(displayItems);
|
||||
unfilteredResults=results;
|
||||
onDataLoaded(filterSearchResults(results), false);
|
||||
onDataLoaded(filterSearchResults(results), type!=null && !results.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -122,7 +122,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
recentsHeader.setVisible(!data.isEmpty());
|
||||
});
|
||||
}else{
|
||||
currentRequest=new GetSearchResults(currentQuery, null, false)
|
||||
currentRequest=new GetSearchResults(currentQuery, null, false, null, 0, 0)
|
||||
.limit(2)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
@@ -378,7 +378,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
|
||||
private void openHashtag(SearchResult res){
|
||||
wrapSuicideDialog(()->{
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag);
|
||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putRecentSearch(res);
|
||||
});
|
||||
}
|
||||
@@ -424,6 +424,8 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
}
|
||||
|
||||
private void onSearchViewEnter(){
|
||||
if(TextUtils.isEmpty(currentQuery) || currentQuery.trim().isEmpty())
|
||||
return;
|
||||
wrapSuicideDialog(()->deliverResult(currentQuery, null));
|
||||
}
|
||||
|
||||
@@ -436,7 +438,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
String q=searchViewHelper.getQuery();
|
||||
if(q.startsWith("#"))
|
||||
q=q.substring(1);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, q, null);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, q);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
||||
|
||||
@Override
|
||||
public void onClick(){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ public class SignupFragment extends ToolbarFragment{
|
||||
@Override
|
||||
public void tail(Node node, int depth){
|
||||
if(node instanceof Element){
|
||||
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null, null, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
|
||||
setTitle(getString(R.string.about_app, getString(R.string.sk_app_name)));
|
||||
AccountSession s=AccountSessionManager.get(accountID);
|
||||
onDataLoaded(List.of(
|
||||
new ListItem<>(R.string.sk_settings_donate, 0, R.drawable.ic_fluent_heart_24_regular, ()->UiUtils.openHashtagTimeline(getActivity(), accountID, getString(R.string.donate_hashtag), null)),
|
||||
new ListItem<>(R.string.sk_settings_donate, 0, R.drawable.ic_fluent_heart_24_regular, ()->UiUtils.openHashtagTimeline(getActivity(), accountID, getString(R.string.donate_hashtag))),
|
||||
new ListItem<>(R.string.sk_settings_contribute, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.repo_url))),
|
||||
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/terms")),
|
||||
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
|
||||
|
||||
@@ -55,6 +55,8 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
onDataLoaded(List.of(
|
||||
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_fluent_settings_24_regular, this::onBehaviorClick),
|
||||
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_fluent_color_24_regular, this::onDisplayClick),
|
||||
new ListItem<>(R.string.settings_privacy, 0, R.drawable.ic_privacy_tip_24px, this::onPrivacyClick),
|
||||
new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_filter_alt_24px, this::onFiltersClick),
|
||||
new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_fluent_alert_24_regular, this::onNotificationsClick),
|
||||
new ListItem<>(R.string.sk_settings_instance, 0, R.drawable.ic_fluent_server_24_regular, this::onInstanceClick),
|
||||
new ListItem<>(getString(R.string.about_app, getString(R.string.sk_app_name)), null, R.drawable.ic_fluent_info_24_regular, this::onAboutClick, null, 0, true),
|
||||
@@ -69,7 +71,9 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
data.add(0, new ListItem<>("Debug settings", null, R.drawable.ic_fluent_wrench_screwdriver_24_regular, ()->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null, 0, true));
|
||||
}
|
||||
|
||||
account.reloadPreferences(null);
|
||||
AccountSession session=AccountSessionManager.get(accountID);
|
||||
session.reloadPreferences(null);
|
||||
session.updateAccountInfo();
|
||||
E.register(this);
|
||||
}
|
||||
|
||||
@@ -133,6 +137,10 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||
Nav.go(getActivity(), SettingsDisplayFragment.class, makeFragmentArgs());
|
||||
}
|
||||
|
||||
private void onPrivacyClick(){
|
||||
Nav.go(getActivity(), SettingsPrivacyFragment.class, makeFragmentArgs());
|
||||
}
|
||||
|
||||
private void onFiltersClick(){
|
||||
Nav.go(getActivity(), SettingsFiltersFragment.class, makeFragmentArgs());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.joinmastodon.android.fragments.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
|
||||
private CheckableListItem<Void> discoverableItem, indexableItem;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.settings_privacy);
|
||||
Account self=AccountSessionManager.get(accountID).self;
|
||||
onDataLoaded(List.of(
|
||||
discoverableItem=new CheckableListItem<>(R.string.settings_discoverable, 0, CheckableListItem.Style.SWITCH, self.discoverable, R.drawable.ic_thumbs_up_down_24px, ()->toggleCheckableItem(discoverableItem)),
|
||||
indexableItem=new CheckableListItem<>(R.string.settings_indexable, 0, CheckableListItem.Style.SWITCH, self.source.indexable!=null ? self.source.indexable : true, R.drawable.ic_search_24px, ()->toggleCheckableItem(indexableItem))
|
||||
));
|
||||
if(self.source.indexable==null)
|
||||
indexableItem.isEnabled=false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){}
|
||||
|
||||
@Override
|
||||
public void onPause(){
|
||||
super.onPause();
|
||||
Account self=AccountSessionManager.get(accountID).self;
|
||||
if(self.discoverable!=discoverableItem.checked || (self.source.indexable!=null && self.source.indexable!=indexableItem.checked)){
|
||||
self.discoverable=discoverableItem.checked;
|
||||
self.source.indexable=indexableItem.checked;
|
||||
AccountSessionManager.get(accountID).savePreferencesLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,18 +153,21 @@ public class SettingsServerFragment extends AppKitFragment{
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
FrameLayout view=tabViews[viewType];
|
||||
((ViewGroup)view.getParent()).removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
FrameLayout view=new FrameLayout(parent.getContext());
|
||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return new SimpleViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||
FrameLayout view=tabViews[position];
|
||||
if(view.getParent() instanceof ViewGroup parent)
|
||||
parent.removeView(view);
|
||||
view.setVisibility(View.VISIBLE);
|
||||
((FrameLayout)holder.itemView).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
Fragment fragment=getFragmentForPage(position);
|
||||
if(!fragment.isAdded()){
|
||||
getChildFragmentManager().beginTransaction().add(holder.itemView.getId(), fragment).commit();
|
||||
getChildFragmentManager().beginTransaction().add(view.getId(), fragment).commit();
|
||||
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||
@Override
|
||||
public boolean onPreDraw(){
|
||||
|
||||
@@ -139,6 +139,7 @@ public class Account extends BaseModel implements Searchable{
|
||||
* When a timed mute will expire, if applicable.
|
||||
*/
|
||||
public Instant muteExpiresAt;
|
||||
public boolean noindex;
|
||||
|
||||
public List<Role> roles;
|
||||
|
||||
@@ -238,6 +239,7 @@ public class Account extends BaseModel implements Searchable{
|
||||
", source="+source+
|
||||
", suspended="+suspended+
|
||||
", muteExpiresAt="+muteExpiresAt+
|
||||
", noindex="+noindex+
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
||||
", following="+following+
|
||||
", history="+history+
|
||||
", statusesCount="+statusesCount+
|
||||
", following="+following+
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -30,4 +31,19 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
||||
public String getID(){
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(this==o) return true;
|
||||
if(o==null || getClass()!=o.getClass()) return false;
|
||||
|
||||
Hashtag hashtag=(Hashtag) o;
|
||||
|
||||
return name.equals(hashtag.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,22 @@ public class Mention extends BaseModel{
|
||||
", url='"+url+'\''+
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(this==o) return true;
|
||||
if(o==null || getClass()!=o.getClass()) return false;
|
||||
|
||||
Mention mention=(Mention) o;
|
||||
|
||||
if(!id.equals(mention.id)) return false;
|
||||
return url.equals(mention.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
int result=id.hashCode();
|
||||
result=31*result+url.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ public class Source extends BaseModel{
|
||||
* The number of pending follow requests.
|
||||
*/
|
||||
public int followRequestCount;
|
||||
public Boolean indexable;
|
||||
public boolean hideCollections;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
@@ -54,6 +56,8 @@ public class Source extends BaseModel{
|
||||
", sensitive="+sensitive+
|
||||
", language='"+language+'\''+
|
||||
", followRequestCount="+followRequestCount+
|
||||
", indexable="+indexable+
|
||||
", hideCollections="+hideCollections+
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,17 @@ import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDese
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||
|
||||
@@ -219,7 +219,7 @@ public class TimelineDefinition {
|
||||
args.putString("listID", listId);
|
||||
args.putBoolean("listIsExclusive", listIsExclusive);
|
||||
} else if (type == TimelineType.HASHTAG) {
|
||||
args.putString("hashtag", hashtagName);
|
||||
args.putString("hashtagName", hashtagName);
|
||||
args.putBoolean("localOnly", hashtagLocalOnly);
|
||||
args.putStringArrayList("any", hashtagAny == null ? new ArrayList<>() : new ArrayList<>(hashtagAny));
|
||||
args.putStringArrayList("all", hashtagAll == null ? new ArrayList<>() : new ArrayList<>(hashtagAll));
|
||||
|
||||
@@ -15,6 +15,7 @@ import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
@@ -53,7 +54,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
||||
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText, Status status) {
|
||||
super(parentID, parentFragment);
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
|
||||
HtmlParser.parseCustomEmoji(ssb, emojis);
|
||||
if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames)
|
||||
HtmlParser.parseCustomEmoji(ssb, emojis);
|
||||
this.text=ssb;
|
||||
emojiHelper.setText(ssb);
|
||||
this.icon=icon;
|
||||
|
||||
@@ -180,10 +180,8 @@ public abstract class StatusDisplayItem{
|
||||
.ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem(
|
||||
parentID, fragment, hashtag.name, List.of(),
|
||||
R.drawable.ic_fluent_number_symbol_20sp_filled, null,
|
||||
i -> {
|
||||
args.putString("hashtag", hashtag.name);
|
||||
Nav.go(fragment.getActivity(), HashtagTimelineFragment.class, args);
|
||||
}, status
|
||||
i->UiUtils.openHashtagTimeline(fragment.getActivity(), accountID, hashtag),
|
||||
status
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
@@ -878,6 +878,8 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(MediaPlayer mp, int width, int height){
|
||||
if(width<=0 || height<=0)
|
||||
return;
|
||||
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) wrap.getLayoutParams();
|
||||
params.width=width;
|
||||
params.height=height;
|
||||
|
||||
@@ -119,6 +119,9 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
||||
|
||||
int width=right-left;
|
||||
int height=bottom-top;
|
||||
if(width==0 || height==0)
|
||||
return;
|
||||
|
||||
float scale=Math.min(width/(float)child.getWidth(), height/(float)child.getHeight());
|
||||
minScale=scale;
|
||||
maxScale=Math.max(3f, height/(float)child.getHeight());
|
||||
@@ -306,8 +309,6 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
|
||||
}, 1f).setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_ALPHA));
|
||||
}
|
||||
}else{
|
||||
if(animatingTransition)
|
||||
Log.w(TAG, "updateViewTransform: ", new Throwable().fillInStackTrace());
|
||||
child.setScaleX(matrixValues[Matrix.MSCALE_X]);
|
||||
child.setScaleY(matrixValues[Matrix.MSCALE_Y]);
|
||||
child.setTranslationX(matrixValues[Matrix.MTRANS_X]);
|
||||
|
||||
@@ -100,9 +100,10 @@ public class HtmlParser{
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> idsByUrl=mentions.stream().collect(Collectors.toMap(m->m.url, m->m.id));
|
||||
Map<String, String> idsByUrl=mentions.stream().distinct().collect(Collectors.toMap(m->m.url, m->m.id));
|
||||
// Hashtags in remote posts have remote URLs, these have local URLs so they don't match.
|
||||
// Map<String, String> tagsByUrl=tags.stream().collect(Collectors.toMap(t->t.url, t->t.name));
|
||||
Map<String, Hashtag> tagsByTag=tags.stream().distinct().collect(Collectors.toMap(t->t.name.toLowerCase(), Function.identity()));
|
||||
|
||||
final SpannableStringBuilder ssb=new SpannableStringBuilder();
|
||||
Jsoup.parseBodyFragment(source).body().traverse(new NodeVisitor(){
|
||||
@@ -115,6 +116,7 @@ public class HtmlParser{
|
||||
}else if(node instanceof Element el){
|
||||
switch(el.nodeName()){
|
||||
case "a" -> {
|
||||
Object linkObject=null;
|
||||
String href=el.attr("href");
|
||||
LinkSpan.Type linkType;
|
||||
String text=el.text();
|
||||
@@ -122,6 +124,7 @@ public class HtmlParser{
|
||||
if(text.startsWith("#")){
|
||||
linkType=LinkSpan.Type.HASHTAG;
|
||||
href=text.substring(1);
|
||||
linkObject=tagsByTag.get(text.substring(1).toLowerCase());
|
||||
}else{
|
||||
linkType=LinkSpan.Type.URL;
|
||||
}
|
||||
@@ -136,7 +139,7 @@ public class HtmlParser{
|
||||
}else{
|
||||
linkType=LinkSpan.Type.URL;
|
||||
}
|
||||
openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID), ssb.length(), el));
|
||||
openSpans.add(new SpanInfo(new LinkSpan(href, null, linkType, accountID, linkObject, text), ssb.length(), el));
|
||||
}
|
||||
case "br" -> ssb.append('\n');
|
||||
case "span" -> {
|
||||
@@ -271,7 +274,7 @@ public class HtmlParser{
|
||||
String url=matcher.group(3);
|
||||
if(TextUtils.isEmpty(matcher.group(4)))
|
||||
url="http://"+url;
|
||||
ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null), matcher.start(3), matcher.end(3), 0);
|
||||
ssb.setSpan(new LinkSpan(url, null, LinkSpan.Type.URL, null, null, null), matcher.start(3), matcher.end(3), 0);
|
||||
}while(matcher.find()); // Find more URLs
|
||||
return ssb;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.text.TextPaint;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.view.View;
|
||||
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
public class LinkSpan extends CharacterStyle {
|
||||
@@ -14,17 +15,15 @@ public class LinkSpan extends CharacterStyle {
|
||||
private String link;
|
||||
private Type type;
|
||||
private String accountID;
|
||||
private Object linkObject;
|
||||
private String text;
|
||||
|
||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID){
|
||||
this(link, listener, type, accountID, null);
|
||||
}
|
||||
|
||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, String text){
|
||||
public LinkSpan(String link, OnLinkClickListener listener, Type type, String accountID, Object linkObject, String text){
|
||||
this.listener=listener;
|
||||
this.link=link;
|
||||
this.type=type;
|
||||
this.accountID=accountID;
|
||||
this.linkObject=linkObject;
|
||||
this.text=text;
|
||||
}
|
||||
|
||||
@@ -42,7 +41,12 @@ public class LinkSpan extends CharacterStyle {
|
||||
switch(getType()){
|
||||
case URL -> UiUtils.openURL(context, accountID, link);
|
||||
case MENTION -> UiUtils.openProfileByID(context, accountID, link);
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(context, accountID, link, null);
|
||||
case HASHTAG -> {
|
||||
if(linkObject instanceof Hashtag ht)
|
||||
UiUtils.openHashtagTimeline(context, accountID, ht);
|
||||
else
|
||||
UiUtils.openHashtagTimeline(context, accountID, text);
|
||||
}
|
||||
case CUSTOM -> listener.onLinkClick(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ import org.joinmastodon.android.model.AccountField;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.ScheduledStatus;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
@@ -445,12 +446,18 @@ public class UiUtils {
|
||||
Nav.go((Activity) context, ProfileFragment.class, args);
|
||||
}
|
||||
|
||||
public static void openHashtagTimeline(Context context, String accountID, String hashtag, @Nullable Boolean following) {
|
||||
Bundle args = new Bundle();
|
||||
public static void openHashtagTimeline(Context context, String accountID, Hashtag hashtag){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("hashtag", hashtag);
|
||||
if (following != null) args.putBoolean("following", following);
|
||||
Nav.go((Activity) context, HashtagTimelineFragment.class, args);
|
||||
args.putParcelable("hashtag", Parcels.wrap(hashtag));
|
||||
Nav.go((Activity)context, HashtagTimelineFragment.class, args);
|
||||
}
|
||||
|
||||
public static void openHashtagTimeline(Context context, String accountID, String hashtag){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putString("hashtagName", hashtag);
|
||||
Nav.go((Activity)context, HashtagTimelineFragment.class, args);
|
||||
}
|
||||
|
||||
public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, Runnable onConfirmed) {
|
||||
@@ -1157,7 +1164,7 @@ public class UiUtils {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() {
|
||||
return Optional.of(new GetSearchResults(query.getQuery(), type, true, null, 0, 0).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
Optional<T> result = extractResult.apply(results);
|
||||
@@ -1254,7 +1261,7 @@ public class UiUtils {
|
||||
}
|
||||
public static MastodonAPIRequest<SearchResults> lookupAccountHandle(Context context, String accountID, Pair<String, Optional<String>> queryHandle, BiConsumer<Class<? extends Fragment>, Bundle> go) {
|
||||
String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse(""));
|
||||
return new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true)
|
||||
return new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true, null, 0, 0)
|
||||
.setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
@@ -1317,7 +1324,7 @@ public class UiUtils {
|
||||
})
|
||||
.execNoAuth(uri.getHost()));
|
||||
} else if (looksLikeFediverseUrl(url)) {
|
||||
return Optional.of(new GetSearchResults(url, null, true)
|
||||
return Optional.of(new GetSearchResults(url, null, true, null, 0, 0)
|
||||
.setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(SearchResults results) {
|
||||
|
||||
@@ -15,15 +15,12 @@ import android.widget.TextView;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.SearchResults;
|
||||
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.FilterChipView;
|
||||
@@ -96,6 +93,24 @@ public class ComposeAutocompleteViewController{
|
||||
outRect.right=V.dp(8);
|
||||
}
|
||||
});
|
||||
// Set empty adapter to prevent NPEs
|
||||
list.setAdapter(new RecyclerView.Adapter<>(){
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
contentView.addView(list, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
emptyButton=new FilterChipView(activity);
|
||||
@@ -222,11 +237,13 @@ public class ComposeAutocompleteViewController{
|
||||
}
|
||||
|
||||
private void doSearchUsers(){
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.ACCOUNTS, false)
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.ACCOUNTS, false, null, 0, 0)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
currentRequest=null;
|
||||
if(mode!=Mode.USERS)
|
||||
return;
|
||||
List<AccountViewModel> oldList=users;
|
||||
users=result.accounts.stream().map(a->new AccountViewModel(a, accountID)).collect(Collectors.toList());
|
||||
if(isLoading){
|
||||
@@ -256,7 +273,7 @@ public class ComposeAutocompleteViewController{
|
||||
}
|
||||
|
||||
private void doSearchHashtags(){
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.HASHTAGS, false)
|
||||
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.HASHTAGS, false, null, 0, 0)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(SearchResults result){
|
||||
|
||||
@@ -75,14 +75,14 @@ public class ComposePollViewController{
|
||||
|
||||
Instance instance=fragment.instance;
|
||||
if (!instance.isAkkoma()) {
|
||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
||||
if(instance!=null && instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
||||
maxPollOptions=instance.configuration.polls.maxOptions;
|
||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
||||
if(instance!=null && instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
||||
maxPollOptionLength=instance.configuration.polls.maxCharactersPerOption;
|
||||
} else {
|
||||
if (instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
||||
if(instance!=null && instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
||||
maxPollOptions=instance.pollLimits.maxOptions;
|
||||
if(instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
||||
if(instance!=null && instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
||||
maxPollOptionLength=instance.pollLimits.maxOptionChars;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class MediaGridLayout extends ViewGroup{
|
||||
|
||||
private static final int GAP=2; // dp
|
||||
private PhotoLayoutHelper.TiledLayoutResult tiledLayout;
|
||||
private int[] columnStarts=new int[10], columnEnds=new int[10], rowStarts=new int[10], rowEnds=new int[10];
|
||||
private int[] columnStarts, columnEnds, rowStarts, rowEnds;
|
||||
|
||||
public MediaGridLayout(Context context){
|
||||
this(context, null);
|
||||
@@ -41,6 +41,14 @@ public class MediaGridLayout extends ViewGroup{
|
||||
width=Math.round(width*(tiledLayout.width/(float)PhotoLayoutHelper.MAX_WIDTH));
|
||||
}
|
||||
|
||||
if(rowStarts==null || rowStarts.length<tiledLayout.rowSizes.length){
|
||||
rowStarts=new int[tiledLayout.rowSizes.length];
|
||||
rowEnds=new int[tiledLayout.rowSizes.length];
|
||||
}
|
||||
if(columnStarts==null || columnStarts.length<tiledLayout.columnSizes.length){
|
||||
columnStarts=new int[tiledLayout.columnSizes.length];
|
||||
columnEnds=new int[tiledLayout.columnSizes.length];
|
||||
}
|
||||
int offset=0;
|
||||
for(int i=0;i<tiledLayout.columnSizes.length;i++){
|
||||
columnStarts[i]=offset;
|
||||
@@ -73,7 +81,7 @@ public class MediaGridLayout extends ViewGroup{
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b){
|
||||
if(tiledLayout==null)
|
||||
if(tiledLayout==null || rowStarts==null)
|
||||
return;
|
||||
|
||||
int maxWidth=UiUtils.MAX_WIDTH;
|
||||
|
||||
Reference in New Issue
Block a user