Add ability to scroll to top by tab click at the Account activity (#1146)
* Issue: tuskyapp#1078 Add ability to scroll to top by tab click at the Account activity * Fix issue with scroll tabs other than current * Update scroll on click behavior * Update code formatting * Remove unused code * Move tab click listener from Fragments to Activities
This commit is contained in:
parent
a2fa49aafb
commit
01234bb94b
8 changed files with 146 additions and 108 deletions
|
@ -41,12 +41,14 @@ import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.keylesspalace.tusky.adapter.AccountFieldAdapter
|
import com.keylesspalace.tusky.adapter.AccountFieldAdapter
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.entity.Account
|
import com.keylesspalace.tusky.entity.Account
|
||||||
import com.keylesspalace.tusky.entity.Relationship
|
import com.keylesspalace.tusky.entity.Relationship
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||||
import com.keylesspalace.tusky.pager.AccountPagerAdapter
|
import com.keylesspalace.tusky.pager.AccountPagerAdapter
|
||||||
import com.keylesspalace.tusky.util.*
|
import com.keylesspalace.tusky.util.*
|
||||||
import com.keylesspalace.tusky.viewmodel.AccountViewModel
|
import com.keylesspalace.tusky.viewmodel.AccountViewModel
|
||||||
|
@ -104,6 +106,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
REQUESTED
|
REQUESTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var adapter: AccountPagerAdapter? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -174,7 +178,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
|
|
||||||
// Add a listener to change the toolbar icon color when it enters/exits its collapsed state.
|
// Add a listener to change the toolbar icon color when it enters/exits its collapsed state.
|
||||||
accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
|
accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
|
||||||
@AttrRes var priorAttribute = R.attr.account_toolbar_icon_tint_uncollapsed
|
@AttrRes
|
||||||
|
var priorAttribute = R.attr.account_toolbar_icon_tint_uncollapsed
|
||||||
|
|
||||||
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
||||||
|
|
||||||
|
@ -250,9 +255,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
accountFieldList.adapter = accountFieldAdapter
|
accountFieldList.adapter = accountFieldAdapter
|
||||||
|
|
||||||
// Setup the tabs and timeline pager.
|
// Setup the tabs and timeline pager.
|
||||||
val adapter = AccountPagerAdapter(supportFragmentManager, accountId)
|
adapter = AccountPagerAdapter(supportFragmentManager, accountId)
|
||||||
val pageTitles = arrayOf(getString(R.string.title_statuses), getString(R.string.title_statuses_with_replies), getString(R.string.title_statuses_pinned), getString(R.string.title_media))
|
val pageTitles = arrayOf(getString(R.string.title_statuses), getString(R.string.title_statuses_with_replies), getString(R.string.title_statuses_pinned), getString(R.string.title_media))
|
||||||
adapter.setPageTitles(pageTitles)
|
adapter?.setPageTitles(pageTitles)
|
||||||
accountFragmentViewPager.pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
accountFragmentViewPager.pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
|
||||||
val pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
|
val pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
|
||||||
R.drawable.tab_page_margin_dark)
|
R.drawable.tab_page_margin_dark)
|
||||||
|
@ -260,10 +265,21 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
accountFragmentViewPager.adapter = adapter
|
accountFragmentViewPager.adapter = adapter
|
||||||
accountFragmentViewPager.offscreenPageLimit = 2
|
accountFragmentViewPager.offscreenPageLimit = 2
|
||||||
accountTabLayout.setupWithViewPager(accountFragmentViewPager)
|
accountTabLayout.setupWithViewPager(accountFragmentViewPager)
|
||||||
|
accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||||
|
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||||
|
tab?.position?.let {
|
||||||
|
(adapter?.getFragment(tab.position) as? ReselectableFragment)?.onReselect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||||
|
|
||||||
|
override fun onTabSelected(tab: TabLayout.Tab?) {}
|
||||||
|
|
||||||
|
})
|
||||||
val accountListClickListener = { v: View ->
|
val accountListClickListener = { v: View ->
|
||||||
val type = when (v.id) {
|
val type = when (v.id) {
|
||||||
R.id.accountFollowers-> AccountListActivity.Type.FOLLOWERS
|
R.id.accountFollowers -> AccountListActivity.Type.FOLLOWERS
|
||||||
R.id.accountFollowing -> AccountListActivity.Type.FOLLOWS
|
R.id.accountFollowing -> AccountListActivity.Type.FOLLOWS
|
||||||
else -> throw AssertionError()
|
else -> throw AssertionError()
|
||||||
}
|
}
|
||||||
|
@ -424,7 +440,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFollowButton() {
|
private fun updateFollowButton() {
|
||||||
if(isSelf) {
|
if (isSelf) {
|
||||||
accountFollowButton.setText(R.string.action_edit_own_profile)
|
accountFollowButton.setText(R.string.action_edit_own_profile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -449,7 +465,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportF
|
||||||
accountFollowButton.show()
|
accountFollowButton.show()
|
||||||
updateFollowButton()
|
updateFollowButton()
|
||||||
|
|
||||||
if(isSelf) {
|
if (isSelf) {
|
||||||
accountFloatingActionButton.hide()
|
accountFloatingActionButton.hide()
|
||||||
} else {
|
} else {
|
||||||
accountFloatingActionButton.show()
|
accountFloatingActionButton.show()
|
||||||
|
|
|
@ -16,20 +16,25 @@
|
||||||
package com.keylesspalace.tusky;
|
package com.keylesspalace.tusky;
|
||||||
|
|
||||||
import androidx.lifecycle.Lifecycle;
|
import androidx.lifecycle.Lifecycle;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
import androidx.emoji.text.EmojiCompat;
|
import androidx.emoji.text.EmojiCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
@ -44,6 +49,7 @@ import com.keylesspalace.tusky.components.conversation.ConversationsRepository;
|
||||||
import com.keylesspalace.tusky.db.AccountEntity;
|
import com.keylesspalace.tusky.db.AccountEntity;
|
||||||
import com.keylesspalace.tusky.entity.Account;
|
import com.keylesspalace.tusky.entity.Account;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||||
import com.keylesspalace.tusky.pager.MainPagerAdapter;
|
import com.keylesspalace.tusky.pager.MainPagerAdapter;
|
||||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||||
import com.keylesspalace.tusky.util.NotificationHelper;
|
import com.keylesspalace.tusky.util.NotificationHelper;
|
||||||
|
@ -114,12 +120,13 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
private ViewPager viewPager;
|
private ViewPager viewPager;
|
||||||
|
|
||||||
private int notificationTabPosition;
|
private int notificationTabPosition;
|
||||||
|
private MainPagerAdapter adapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if(accountManager.getActiveAccount() == null) {
|
if (accountManager.getActiveAccount() == null) {
|
||||||
// will be redirected to LoginActivity by BaseActivity
|
// will be redirected to LoginActivity by BaseActivity
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,6 +216,12 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTabReselected(TabLayout.Tab tab) {
|
public void onTabReselected(TabLayout.Tab tab) {
|
||||||
|
if (adapter != null) {
|
||||||
|
Fragment fragment = adapter.getFragment(tab.getPosition());
|
||||||
|
if (fragment instanceof ReselectableFragment) {
|
||||||
|
((ReselectableFragment) fragment).onReselect();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -410,7 +423,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
private void setupTabs(boolean selectNotificationTab) {
|
private void setupTabs(boolean selectNotificationTab) {
|
||||||
List<TabData> tabs = accountManager.getActiveAccount().getTabPreferences();
|
List<TabData> tabs = accountManager.getActiveAccount().getTabPreferences();
|
||||||
|
|
||||||
MainPagerAdapter adapter = new MainPagerAdapter(tabs, getSupportFragmentManager());
|
adapter = new MainPagerAdapter(tabs, getSupportFragmentManager());
|
||||||
viewPager.setAdapter(adapter);
|
viewPager.setAdapter(adapter);
|
||||||
|
|
||||||
tabLayout.setupWithViewPager(viewPager);
|
tabLayout.setupWithViewPager(viewPager);
|
||||||
|
@ -420,9 +433,9 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
.setIcon(tabs.get(i).getIcon())
|
.setIcon(tabs.get(i).getIcon())
|
||||||
.setContentDescription(tabs.get(i).getText());
|
.setContentDescription(tabs.get(i).getText());
|
||||||
tabLayout.addTab(tab);
|
tabLayout.addTab(tab);
|
||||||
if(tabs.get(i).getId().equals(TabDataKt.NOTIFICATIONS)) {
|
if (tabs.get(i).getId().equals(TabDataKt.NOTIFICATIONS)) {
|
||||||
notificationTabPosition = i;
|
notificationTabPosition = i;
|
||||||
if(selectNotificationTab) {
|
if (selectNotificationTab) {
|
||||||
tab.select();
|
tab.select();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -544,7 +557,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
.withSelectable(false)
|
.withSelectable(false)
|
||||||
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
.withIcon(GoogleMaterial.Icon.gmd_person_add);
|
||||||
drawer.addItemAtPosition(followRequestsItem, 3);
|
drawer.addItemAtPosition(followRequestsItem, 3);
|
||||||
} else if(!me.getLocked()){
|
} else if (!me.getLocked()) {
|
||||||
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS);
|
drawer.removeItem(DRAWER_ITEM_FOLLOW_REQUESTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,7 +569,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
|
|
||||||
List<AccountEntity> allAccounts = accountManager.getAllAccountsOrderedByActive();
|
List<AccountEntity> allAccounts = accountManager.getAllAccountsOrderedByActive();
|
||||||
|
|
||||||
List<IProfile> profiles = new ArrayList<>(allAccounts.size()+1);
|
List<IProfile> profiles = new ArrayList<>(allAccounts.size() + 1);
|
||||||
|
|
||||||
for (AccountEntity acc : allAccounts) {
|
for (AccountEntity acc : allAccounts) {
|
||||||
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(acc.getDisplayName(), acc.getEmojis(), headerResult.getView());
|
CharSequence emojifiedName = CustomEmojiHelper.emojifyString(acc.getDisplayName(), acc.getEmojis(), headerResult.getView());
|
||||||
|
@ -574,7 +587,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
}
|
}
|
||||||
|
|
||||||
// reuse the already existing "add account" item
|
// reuse the already existing "add account" item
|
||||||
for (IProfile profile: headerResult.getProfiles()) {
|
for (IProfile profile : headerResult.getProfiles()) {
|
||||||
if (profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT) {
|
if (profile.getIdentifier() == DRAWER_ITEM_ADD_ACCOUNT) {
|
||||||
profiles.add(profile);
|
profiles.add(profile);
|
||||||
break;
|
break;
|
||||||
|
@ -599,5 +612,4 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
|
||||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||||
return fragmentInjector;
|
return fragmentInjector;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -34,6 +34,7 @@ import com.keylesspalace.tusky.db.AppDatabase
|
||||||
import com.keylesspalace.tusky.di.Injectable
|
import com.keylesspalace.tusky.di.Injectable
|
||||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||||
import com.keylesspalace.tusky.fragment.SFragment
|
import com.keylesspalace.tusky.fragment.SFragment
|
||||||
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||||
import com.keylesspalace.tusky.util.NetworkState
|
import com.keylesspalace.tusky.util.NetworkState
|
||||||
import com.keylesspalace.tusky.util.ThemeUtils
|
import com.keylesspalace.tusky.util.ThemeUtils
|
||||||
|
@ -41,7 +42,7 @@ import com.keylesspalace.tusky.util.hide
|
||||||
import kotlinx.android.synthetic.main.fragment_timeline.*
|
import kotlinx.android.synthetic.main.fragment_timeline.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ConversationsFragment : SFragment(), StatusActionListener, Injectable {
|
class ConversationsFragment : SFragment(), StatusActionListener, Injectable, ReselectableFragment {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelFactory
|
lateinit var viewModelFactory: ViewModelFactory
|
||||||
|
@ -52,6 +53,8 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable {
|
||||||
|
|
||||||
private lateinit var adapter: ConversationAdapter
|
private lateinit var adapter: ConversationAdapter
|
||||||
|
|
||||||
|
private var layoutManager: LinearLayoutManager? = null
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)[ConversationsViewModel::class.java]
|
viewModel = ViewModelProviders.of(this, viewModelFactory)[ConversationsViewModel::class.java]
|
||||||
|
|
||||||
|
@ -67,10 +70,11 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable {
|
||||||
val mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true
|
val mediaPreviewEnabled = account?.mediaPreviewEnabled ?: true
|
||||||
|
|
||||||
|
|
||||||
adapter = ConversationAdapter(useAbsoluteTime, mediaPreviewEnabled,this, ::onTopLoaded, viewModel::retry)
|
adapter = ConversationAdapter(useAbsoluteTime, mediaPreviewEnabled, this, ::onTopLoaded, viewModel::retry)
|
||||||
|
|
||||||
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
recyclerView.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
|
||||||
recyclerView.layoutManager = LinearLayoutManager(view.context)
|
layoutManager = LinearLayoutManager(view.context)
|
||||||
|
recyclerView.layoutManager = layoutManager
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||||
|
|
||||||
|
@ -172,6 +176,17 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun jumpToTop() {
|
||||||
|
if (isAdded) {
|
||||||
|
layoutManager?.scrollToPosition(0)
|
||||||
|
recyclerView.stopScroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReselect() {
|
||||||
|
jumpToTop()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance() = ConversationsFragment()
|
fun newInstance() = ConversationsFragment()
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,6 @@ import android.view.ViewGroup;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
|
||||||
import com.keylesspalace.tusky.MainActivity;
|
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
||||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder;
|
||||||
|
@ -43,6 +41,7 @@ import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Notification;
|
import com.keylesspalace.tusky.entity.Notification;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
import com.keylesspalace.tusky.util.CollectionUtil;
|
import com.keylesspalace.tusky.util.CollectionUtil;
|
||||||
import com.keylesspalace.tusky.util.Either;
|
import com.keylesspalace.tusky.util.Either;
|
||||||
|
@ -97,7 +96,7 @@ public class NotificationsFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener,
|
SwipeRefreshLayout.OnRefreshListener,
|
||||||
StatusActionListener,
|
StatusActionListener,
|
||||||
NotificationsAdapter.NotificationActionListener,
|
NotificationsAdapter.NotificationActionListener,
|
||||||
Injectable {
|
Injectable, ReselectableFragment {
|
||||||
private static final String TAG = "NotificationF"; // logging tag
|
private static final String TAG = "NotificationF"; // logging tag
|
||||||
|
|
||||||
private static final int LOAD_AT_ONCE = 30;
|
private static final int LOAD_AT_ONCE = 30;
|
||||||
|
@ -138,7 +137,6 @@ public class NotificationsFragment extends SFragment implements
|
||||||
private LinearLayoutManager layoutManager;
|
private LinearLayoutManager layoutManager;
|
||||||
private EndlessOnScrollListener scrollListener;
|
private EndlessOnScrollListener scrollListener;
|
||||||
private NotificationsAdapter adapter;
|
private NotificationsAdapter adapter;
|
||||||
private TabLayout.OnTabSelectedListener onTabSelectedListener;
|
|
||||||
private boolean hideFab;
|
private boolean hideFab;
|
||||||
private boolean topLoading;
|
private boolean topLoading;
|
||||||
private boolean bottomLoading;
|
private boolean bottomLoading;
|
||||||
|
@ -250,28 +248,9 @@ public class NotificationsFragment extends SFragment implements
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
Activity activity = getActivity();
|
||||||
MainActivity activity = (MainActivity) getActivity();
|
|
||||||
if (activity == null) throw new AssertionError("Activity is null");
|
if (activity == null) throw new AssertionError("Activity is null");
|
||||||
|
|
||||||
// MainActivity's layout is guaranteed to be inflated until onCreate returns.
|
|
||||||
TabLayout layout = activity.findViewById(R.id.tab_layout);
|
|
||||||
onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onTabSelected(TabLayout.Tab tab) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabUnselected(TabLayout.Tab tab) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabReselected(TabLayout.Tab tab) {
|
|
||||||
jumpToTop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
layout.addOnTabSelectedListener(onTabSelectedListener);
|
|
||||||
|
|
||||||
/* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't
|
/* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't
|
||||||
* guaranteed to be set until then.
|
* guaranteed to be set until then.
|
||||||
* Use a modified scroll listener that both loads more notificationsEnabled as it goes, and hides
|
* Use a modified scroll listener that both loads more notificationsEnabled as it goes, and hides
|
||||||
|
@ -323,19 +302,6 @@ public class NotificationsFragment extends SFragment implements
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
Activity activity = getActivity();
|
|
||||||
if (activity == null) {
|
|
||||||
Log.e(TAG, "Activity is null");
|
|
||||||
} else {
|
|
||||||
TabLayout tabLayout = activity.findViewById(R.id.tab_layout);
|
|
||||||
tabLayout.removeOnTabSelectedListener(onTabSelectedListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
swipeRefreshLayout.setEnabled(true);
|
swipeRefreshLayout.setEnabled(true);
|
||||||
|
@ -634,8 +600,10 @@ public class NotificationsFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void jumpToTop() {
|
private void jumpToTop() {
|
||||||
layoutManager.scrollToPosition(0);
|
if (isAdded()) {
|
||||||
scrollListener.reset();
|
layoutManager.scrollToPosition(0);
|
||||||
|
scrollListener.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendFetchNotificationsRequest(String fromId, String uptoId,
|
private void sendFetchNotificationsRequest(String fromId, String uptoId,
|
||||||
|
@ -974,4 +942,9 @@ public class NotificationsFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReselect() {
|
||||||
|
jumpToTop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,6 @@ import android.view.ViewGroup;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import com.keylesspalace.tusky.AccountListActivity;
|
import com.keylesspalace.tusky.AccountListActivity;
|
||||||
import com.keylesspalace.tusky.BaseActivity;
|
import com.keylesspalace.tusky.BaseActivity;
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
|
@ -49,6 +47,7 @@ import com.keylesspalace.tusky.di.Injectable;
|
||||||
import com.keylesspalace.tusky.entity.Filter;
|
import com.keylesspalace.tusky.entity.Filter;
|
||||||
import com.keylesspalace.tusky.entity.Status;
|
import com.keylesspalace.tusky.entity.Status;
|
||||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||||
|
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||||
import com.keylesspalace.tusky.network.MastodonApi;
|
import com.keylesspalace.tusky.network.MastodonApi;
|
||||||
import com.keylesspalace.tusky.repository.Placeholder;
|
import com.keylesspalace.tusky.repository.Placeholder;
|
||||||
|
@ -66,8 +65,8 @@ import com.keylesspalace.tusky.view.BackgroundMessageView;
|
||||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener;
|
import com.keylesspalace.tusky.view.EndlessOnScrollListener;
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -94,6 +93,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import at.connyduck.sparkbutton.helpers.Utils;
|
import at.connyduck.sparkbutton.helpers.Utils;
|
||||||
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.collections.CollectionsKt;
|
import kotlin.collections.CollectionsKt;
|
||||||
|
@ -107,7 +107,7 @@ import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvid
|
||||||
public class TimelineFragment extends SFragment implements
|
public class TimelineFragment extends SFragment implements
|
||||||
SwipeRefreshLayout.OnRefreshListener,
|
SwipeRefreshLayout.OnRefreshListener,
|
||||||
StatusActionListener,
|
StatusActionListener,
|
||||||
Injectable {
|
Injectable, ReselectableFragment {
|
||||||
private static final String TAG = "TimelineF"; // logging tag
|
private static final String TAG = "TimelineF"; // logging tag
|
||||||
private static final String KIND_ARG = "kind";
|
private static final String KIND_ARG = "kind";
|
||||||
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id";
|
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id";
|
||||||
|
@ -152,7 +152,6 @@ public class TimelineFragment extends SFragment implements
|
||||||
private String hashtagOrId;
|
private String hashtagOrId;
|
||||||
private LinearLayoutManager layoutManager;
|
private LinearLayoutManager layoutManager;
|
||||||
private EndlessOnScrollListener scrollListener;
|
private EndlessOnScrollListener scrollListener;
|
||||||
private TabLayout.OnTabSelectedListener onTabSelectedListener;
|
|
||||||
private boolean filterRemoveReplies;
|
private boolean filterRemoveReplies;
|
||||||
private boolean filterRemoveReblogs;
|
private boolean filterRemoveReblogs;
|
||||||
private boolean filterRemoveRegex;
|
private boolean filterRemoveRegex;
|
||||||
|
@ -320,7 +319,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(@NonNull Call<List<Filter>> call, @NonNull Response<List<Filter>> response) {
|
public void onResponse(@NonNull Call<List<Filter>> call, @NonNull Response<List<Filter>> response) {
|
||||||
List<Filter> filterList = response.body();
|
List<Filter> filterList = response.body();
|
||||||
if(response.isSuccessful() && filterList != null) {
|
if (response.isSuccessful() && filterList != null) {
|
||||||
applyFilters(filterList, refresh);
|
applyFilters(filterList, refresh);
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Error getting filters from server");
|
Log.e(TAG, "Error getting filters from server");
|
||||||
|
@ -352,7 +351,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
|
|
||||||
private static boolean filterContextMatchesKind(Kind kind, List<String> filterContext) {
|
private static boolean filterContextMatchesKind(Kind kind, List<String> filterContext) {
|
||||||
// home, notifications, public, thread
|
// home, notifications, public, thread
|
||||||
switch(kind) {
|
switch (kind) {
|
||||||
case HOME:
|
case HOME:
|
||||||
return filterContext.contains(Filter.HOME);
|
return filterContext.contains(Filter.HOME);
|
||||||
case PUBLIC_FEDERATED:
|
case PUBLIC_FEDERATED:
|
||||||
|
@ -436,27 +435,6 @@ public class TimelineFragment extends SFragment implements
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
if (jumpToTopAllowed()) {
|
|
||||||
TabLayout layout = requireActivity().findViewById(R.id.tab_layout);
|
|
||||||
if (layout != null) {
|
|
||||||
onTabSelectedListener = new TabLayout.OnTabSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onTabSelected(TabLayout.Tab tab) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabUnselected(TabLayout.Tab tab) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabReselected(TabLayout.Tab tab) {
|
|
||||||
jumpToTop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
layout.addOnTabSelectedListener(onTabSelectedListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't
|
/* This is delayed until onActivityCreated solely because MainActivity.composeButton isn't
|
||||||
* guaranteed to be set until then. */
|
* guaranteed to be set until then. */
|
||||||
if (actionButtonPresent()) {
|
if (actionButtonPresent()) {
|
||||||
|
@ -543,17 +521,6 @@ public class TimelineFragment extends SFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
if (jumpToTopAllowed()) {
|
|
||||||
TabLayout tabLayout = requireActivity().findViewById(R.id.tab_layout);
|
|
||||||
if (tabLayout != null) {
|
|
||||||
tabLayout.removeOnTabSelectedListener(onTabSelectedListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
swipeRefreshLayout.setEnabled(true);
|
swipeRefreshLayout.setEnabled(true);
|
||||||
|
@ -854,7 +821,7 @@ public class TimelineFragment extends SFragment implements
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(statuses.size() == 0) {
|
if (statuses.size() == 0) {
|
||||||
sendInitialRequest();
|
sendInitialRequest();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -895,19 +862,17 @@ public class TimelineFragment extends SFragment implements
|
||||||
sendFetchTimelineRequest(null, null, null, FetchEnd.BOTTOM, -1);
|
sendFetchTimelineRequest(null, null, null, FetchEnd.BOTTOM, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean jumpToTopAllowed() {
|
|
||||||
return kind != Kind.TAG && kind != Kind.FAVOURITES;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean actionButtonPresent() {
|
private boolean actionButtonPresent() {
|
||||||
return kind != Kind.TAG && kind != Kind.FAVOURITES &&
|
return kind != Kind.TAG && kind != Kind.FAVOURITES &&
|
||||||
getActivity() instanceof ActionButtonActivity;
|
getActivity() instanceof ActionButtonActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void jumpToTop() {
|
private void jumpToTop() {
|
||||||
layoutManager.scrollToPosition(0);
|
if (isAdded()) {
|
||||||
recyclerView.stopScroll();
|
layoutManager.scrollToPosition(0);
|
||||||
scrollListener.reset();
|
recyclerView.stopScroll();
|
||||||
|
scrollListener.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Call<List<Status>> getFetchCallByTimelineType(Kind kind, String tagOrId, String fromId,
|
private Call<List<Status>> getFetchCallByTimelineType(Kind kind, String tagOrId, String fromId,
|
||||||
|
@ -1325,13 +1290,12 @@ public class TimelineFragment extends SFragment implements
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Object getChangePayload(@NonNull StatusViewData oldItem, @NonNull StatusViewData newItem) {
|
public Object getChangePayload(@NonNull StatusViewData oldItem, @NonNull StatusViewData newItem) {
|
||||||
if (oldItem.deepEquals(newItem)){
|
if (oldItem.deepEquals(newItem)) {
|
||||||
//If items are equal - update timestamp only
|
//If items are equal - update timestamp only
|
||||||
List<String> payload = new ArrayList<>();
|
List<String> payload = new ArrayList<>();
|
||||||
payload.add(StatusBaseViewHolder.Key.KEY_CREATED);
|
payload.add(StatusBaseViewHolder.Key.KEY_CREATED);
|
||||||
return payload;
|
return payload;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
// If items are different - update a whole view holder
|
// If items are different - update a whole view holder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1362,5 +1326,8 @@ public class TimelineFragment extends SFragment implements
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReselect() {
|
||||||
|
jumpToTop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.keylesspalace.tusky.interfaces
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by pandasoft (joelpyska1@gmail.com) on 04/04/2019.
|
||||||
|
*/
|
||||||
|
interface ReselectableFragment {
|
||||||
|
/**
|
||||||
|
* Call this method when tab reselected
|
||||||
|
*/
|
||||||
|
fun onReselect()
|
||||||
|
}
|
|
@ -15,17 +15,25 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.pager;
|
package com.keylesspalace.tusky.pager;
|
||||||
|
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.keylesspalace.tusky.fragment.AccountMediaFragment;
|
import com.keylesspalace.tusky.fragment.AccountMediaFragment;
|
||||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentPagerAdapter;
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
|
||||||
public class AccountPagerAdapter extends FragmentPagerAdapter {
|
public class AccountPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
private static final int TAB_COUNT = 4;
|
||||||
private String accountId;
|
private String accountId;
|
||||||
private String[] pageTitles;
|
private String[] pageTitles;
|
||||||
|
|
||||||
|
private SparseArray<Fragment> fragments = new SparseArray<>(TAB_COUNT);
|
||||||
|
|
||||||
public AccountPagerAdapter(FragmentManager manager, String accountId) {
|
public AccountPagerAdapter(FragmentManager manager, String accountId) {
|
||||||
super(manager);
|
super(manager);
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
|
@ -58,11 +66,31 @@ public class AccountPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return 4;
|
return TAB_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Object instantiateItem(@NonNull ViewGroup container, int position) {
|
||||||
|
Object fragment = super.instantiateItem(container, position);
|
||||||
|
if (fragment instanceof Fragment)
|
||||||
|
fragments.put(position, (Fragment) fragment);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
|
||||||
|
super.destroyItem(container, position, object);
|
||||||
|
fragments.remove(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
return pageTitles[position];
|
return pageTitles[position];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Fragment getFragment(int position) {
|
||||||
|
return fragments.get(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
|
|
||||||
package com.keylesspalace.tusky.pager
|
package com.keylesspalace.tusky.pager
|
||||||
|
|
||||||
|
import android.util.SparseArray
|
||||||
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.FragmentPagerAdapter
|
import androidx.fragment.app.FragmentPagerAdapter
|
||||||
|
@ -22,6 +24,7 @@ import androidx.viewpager.widget.PagerAdapter
|
||||||
import com.keylesspalace.tusky.TabData
|
import com.keylesspalace.tusky.TabData
|
||||||
|
|
||||||
class MainPagerAdapter(val tabs: List<TabData>, manager: FragmentManager) : FragmentPagerAdapter(manager) {
|
class MainPagerAdapter(val tabs: List<TabData>, manager: FragmentManager) : FragmentPagerAdapter(manager) {
|
||||||
|
private val fragments = SparseArray<Fragment>(tabs.size)
|
||||||
|
|
||||||
override fun getItem(position: Int): Fragment {
|
override fun getItem(position: Int): Fragment {
|
||||||
val tab = tabs[position]
|
val tab = tabs[position]
|
||||||
|
@ -44,4 +47,17 @@ class MainPagerAdapter(val tabs: List<TabData>, manager: FragmentManager) : Frag
|
||||||
return PagerAdapter.POSITION_NONE
|
return PagerAdapter.POSITION_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||||
|
val fragment = super.instantiateItem(container, position)
|
||||||
|
if (fragment is Fragment)
|
||||||
|
fragments.put(position, fragment)
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||||
|
super.destroyItem(container, position, `object`)
|
||||||
|
fragments.remove(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFragment(position: Int): Fragment? = fragments[position]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue