Migrate to ViewPager2 (#1544)

* migrate MainActivty to ViewPager2

* migrate AccountActivty to ViewPager2

* migrate ViewMediaActivty to ViewPager2

* migrate SearchActivity to ViewPager2

* checkin missing AccountPagerAdapter file

* remove unused class ImageViewPager

* replace SparseArray with MutableList
This commit is contained in:
Konrad Pozniak 2019-11-06 20:17:53 +01:00 committed by GitHub
parent 20e406c413
commit 4d4c4cdb3d
31 changed files with 213 additions and 349 deletions

View File

@ -114,6 +114,7 @@ dependencies {
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.sharetarget:sharetarget:1.0.0-beta01'
implementation "androidx.viewpager2:viewpager2:1.0.0-rc01"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"

View File

@ -37,6 +37,7 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.viewpager2.widget.MarginPageTransformer
import com.bumptech.glide.Glide
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
@ -45,6 +46,7 @@ import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.keylesspalace.tusky.adapter.AccountFieldAdapter
import com.keylesspalace.tusky.components.report.ReportActivity
import com.keylesspalace.tusky.di.ViewModelFactory
@ -187,16 +189,21 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
*/
private fun setupTabs() {
// Setup the tabs and timeline pager.
adapter = AccountPagerAdapter(supportFragmentManager, viewModel.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))
adapter.setPageTitles(pageTitles)
accountFragmentViewPager.pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
val pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
R.drawable.tab_page_margin_dark)
accountFragmentViewPager.setPageMarginDrawable(pageMarginDrawable)
adapter = AccountPagerAdapter(this, viewModel.accountId)
accountFragmentViewPager.adapter = adapter
accountFragmentViewPager.offscreenPageLimit = 2
accountTabLayout.setupWithViewPager(accountFragmentViewPager)
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))
TabLayoutMediator(accountTabLayout, accountFragmentViewPager) {
tab, position ->
tab.text = pageTitles[position]
}.attach()
val pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
accountFragmentViewPager.setPageTransformer(MarginPageTransformer(pageMargin))
accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
tab?.position?.let { position ->

View File

@ -33,11 +33,13 @@ import androidx.emoji.text.EmojiCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import com.bumptech.glide.Glide;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.keylesspalace.tusky.appstore.CacheUpdater;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent;
@ -53,7 +55,6 @@ import com.keylesspalace.tusky.pager.MainPagerAdapter;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.keylesspalace.tusky.util.NotificationHelper;
import com.keylesspalace.tusky.util.ShareShortcutHelper;
import com.keylesspalace.tusky.util.ThemeUtils;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.materialdrawer.AccountHeader;
import com.mikepenz.materialdrawer.AccountHeaderBuilder;
@ -114,7 +115,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
private AccountHeader headerResult;
private Drawer drawer;
private TabLayout tabLayout;
private ViewPager viewPager;
private ViewPager2 viewPager;
private int notificationTabPosition;
private MainPagerAdapter adapter;
@ -207,10 +208,7 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
setupTabs(showNotificationTab);
int pageMargin = getResources().getDimensionPixelSize(R.dimen.tab_page_margin);
viewPager.setPageMargin(pageMargin);
Drawable pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
R.drawable.tab_page_margin_dark);
viewPager.setPageMarginDrawable(pageMarginDrawable);
viewPager.setPageTransformer(new MarginPageTransformer(pageMargin));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
@ -467,10 +465,11 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
private void setupTabs(boolean selectNotificationTab) {
List<TabData> tabs = accountManager.getActiveAccount().getTabPreferences();
adapter = new MainPagerAdapter(tabs, getSupportFragmentManager());
adapter = new MainPagerAdapter(tabs, this);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { }).attach();
tabLayout.removeAllTabs();
for (int i = 0; i < tabs.size(); i++) {
TabLayout.Tab tab = tabLayout.newTab()

View File

@ -37,9 +37,10 @@ import android.view.View
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import com.bumptech.glide.request.FutureTarget
import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID
@ -110,23 +111,23 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
// Adapter is actually of existential type PageAdapter & SharedElementsTransitionListener
// but it cannot be expressed and if I don't specify type explicitly compilation fails
// (probably a bug in compiler)
val adapter: PagerAdapter = if (attachments != null) {
val adapter: ViewMediaAdapter = if (attachments != null) {
val realAttachs = attachments!!.map(AttachmentViewData::attachment)
// Setup the view pager.
ImagePagerAdapter(supportFragmentManager, realAttachs, initialPosition)
ImagePagerAdapter(this, realAttachs, initialPosition)
} else {
val avatarUrl = intent.getStringExtra(EXTRA_AVATAR_URL)
?: throw IllegalArgumentException("attachment list or avatar url has to be set")
AvatarImagePagerAdapter(supportFragmentManager, avatarUrl)
AvatarImagePagerAdapter(this, avatarUrl)
}
viewPager.adapter = adapter
viewPager.currentItem = initialPosition
viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
viewPager.setCurrentItem(initialPosition, false)
viewPager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
toolbar.title = adapter.getPageTitle(position)
toolbar.title = getPageTitle(position)
}
})
@ -136,7 +137,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowHomeEnabled(true)
actionBar.title = adapter.getPageTitle(initialPosition)
actionBar.title = getPageTitle(initialPosition)
}
toolbar.setNavigationOnClickListener { supportFinishAfterTransition() }
toolbar.setOnMenuItemClickListener { item: MenuItem ->
@ -153,7 +154,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
window.statusBarColor = Color.BLACK
window.sharedElementEnterTransition.addListener(object : NoopTransitionListener {
override fun onTransitionEnd(transition: Transition) {
(adapter as SharedElementTransitionListener).onTransitionEnd()
adapter.onTransitionEnd(viewPager.currentItem)
window.sharedElementEnterTransition.removeListener(this)
}
})
@ -198,6 +199,13 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
.start()
}
private fun getPageTitle(position: Int): CharSequence {
if(attachments == null) {
return ""
}
return String.format(Locale.getDefault(), "%d/%d", position + 1, attachments?.size)
}
private fun downloadMedia() {
val url = attachments!![viewPager.currentItem].attachment.url
val filename = Uri.parse(url).lastPathSegment
@ -323,8 +331,8 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
}
}
interface SharedElementTransitionListener {
fun onTransitionEnd()
abstract class ViewMediaAdapter(activity: FragmentActivity): FragmentStateAdapter(activity) {
abstract fun onTransitionEnd(position: Int)
}
interface NoopTransitionListener : Transition.TransitionListener {

View File

@ -23,6 +23,7 @@ import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.ViewModelProviders
import com.google.android.material.tabs.TabLayoutMediator
import com.keylesspalace.tusky.BottomSheetActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter
@ -57,8 +58,12 @@ class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, Ha
}
private fun setupPages() {
pages.adapter = SearchPagerAdapter(this, supportFragmentManager)
tabs.setupWithViewPager(pages)
pages.adapter = SearchPagerAdapter(this)
TabLayoutMediator(tabs, pages) {
tab, position ->
tab.text = getPageTitle(position)
}.attach()
}
override fun onNewIntent(intent: Intent) {
@ -99,6 +104,15 @@ class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, Ha
return false
}
private fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> getString(R.string.title_statuses)
1 -> getString(R.string.title_accounts)
2 -> getString(R.string.title_hashtags_dialog)
else -> throw IllegalArgumentException("Unknown page index: $position")
}
}
private fun handleIntent(intent: Intent) {
if (Intent.ACTION_SEARCH == intent.action) {
viewModel.currentQuery = intent.getStringExtra(SearchManager.QUERY)

View File

@ -15,18 +15,16 @@
package com.keylesspalace.tusky.components.search.adapter
import android.content.Context
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.search.SearchType
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.keylesspalace.tusky.components.search.fragments.SearchAccountsFragment
import com.keylesspalace.tusky.components.search.fragments.SearchHashtagsFragment
import com.keylesspalace.tusky.components.search.fragments.SearchStatusesFragment
class SearchPagerAdapter(private val context: Context, manager: FragmentManager) : FragmentPagerAdapter(manager) {
override fun getItem(position: Int): Fragment {
class SearchPagerAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> SearchStatusesFragment.newInstance()
1 -> SearchAccountsFragment.newInstance()
@ -35,14 +33,6 @@ class SearchPagerAdapter(private val context: Context, manager: FragmentManager)
}
}
override fun getPageTitle(position: Int): CharSequence? {
return when (position) {
0 -> context.getString(R.string.title_statuses)
1 -> context.getString(R.string.title_accounts)
2 -> context.getString(R.string.title_hashtags_dialog)
else -> throw IllegalArgumentException("Unknown page index: $position")
}
}
override fun getItemCount() = 3
override fun getCount(): Int = 3
}

View File

@ -81,7 +81,6 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
private var currentCall: Call<List<Status>>? = null
private val statuses = mutableListOf<Status>()
private var fetchingStatus = FetchingStatus.NOT_FETCHING
private var isVisibleToUser: Boolean = false
private lateinit var accountId: String
@ -216,7 +215,7 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
}
})
if (isVisibleToUser) doInitialLoadingIfNeeded()
doInitialLoadingIfNeeded()
}
private fun refresh() {
@ -235,14 +234,6 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
topProgressBar?.show()
}
// That's sort of an optimization to only load media once user has opened the tab
// Attention: can be called before *any* lifecycle method!
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
this.isVisibleToUser = isVisibleToUser
if (isVisibleToUser && isAdded) doInitialLoadingIfNeeded()
}
private fun doInitialLoadingIfNeeded() {
if (isAdded) {
statusView.hide()

View File

@ -18,13 +18,12 @@ package com.keylesspalace.tusky.fragment
import android.os.Bundle
import android.text.TextUtils
import android.widget.TextView
import com.keylesspalace.tusky.SharedElementTransitionListener
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.visible
abstract class ViewMediaFragment : BaseFragment(), SharedElementTransitionListener {
abstract class ViewMediaFragment : BaseFragment() {
private var toolbarVisibiltyDisposable: Function0<Boolean>? = null
abstract fun setupMediaView(url: String, previewUrl: String?)
@ -71,6 +70,8 @@ abstract class ViewMediaFragment : BaseFragment(), SharedElementTransitionListen
}
}
abstract fun onTransitionEnd()
protected fun finalizeViewSetup(url: String, previewUrl: String?, description: String?) {
val mediaActivity = activity as ViewMediaActivity
setupMediaView(url, previewUrl)

View File

@ -1,120 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.pager;
import android.util.SparseArray;
import android.view.ViewGroup;
import com.keylesspalace.tusky.fragment.AccountMediaFragment;
import com.keylesspalace.tusky.fragment.TimelineFragment;
import com.keylesspalace.tusky.interfaces.RefreshableFragment;
import java.util.HashSet;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class AccountPagerAdapter extends FragmentPagerAdapter {
private static final int TAB_COUNT = 4;
private String accountId;
private String[] pageTitles;
private SparseArray<Fragment> fragments = new SparseArray<>(TAB_COUNT);
private final Set<Integer> pagesToRefresh = new HashSet<>();
public AccountPagerAdapter(FragmentManager manager, String accountId) {
super(manager);
this.accountId = accountId;
}
public void setPageTitles(String[] titles) {
pageTitles = titles;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0: {
return TimelineFragment.newInstance(TimelineFragment.Kind.USER, accountId,false);
}
case 1: {
return TimelineFragment.newInstance(TimelineFragment.Kind.USER_WITH_REPLIES, accountId,false);
}
case 2: {
return TimelineFragment.newInstance(TimelineFragment.Kind.USER_PINNED, accountId,false);
}
case 3: {
return AccountMediaFragment.newInstance(accountId,false);
}
default: {
throw new AssertionError("Page " + position + " is out of AccountPagerAdapter bounds");
}
}
}
@Override
public int getCount() {
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);
if (pagesToRefresh.contains(position)) {
if (fragment instanceof RefreshableFragment)
((RefreshableFragment) fragment).refreshContent();
pagesToRefresh.remove(position);
}
return fragment;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.destroyItem(container, position, object);
fragments.remove(position);
}
@Override
public CharSequence getPageTitle(int position) {
return pageTitles[position];
}
@Nullable
public Fragment getFragment(int position) {
return fragments.get(position);
}
public void refreshContent(){
for (int i=0;i<getCount();i++){
Fragment fragment = getFragment(i);
if (fragment instanceof RefreshableFragment){
((RefreshableFragment) fragment).refreshContent();
}
else{
pagesToRefresh.add(i);
}
}
}
}

View File

@ -0,0 +1,65 @@
/* Copyright 2019 Tusky Contributors
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.pager
import androidx.fragment.app.*
import com.keylesspalace.tusky.fragment.AccountMediaFragment
import com.keylesspalace.tusky.fragment.TimelineFragment
import com.keylesspalace.tusky.interfaces.RefreshableFragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import java.lang.ref.WeakReference
class AccountPagerAdapter(
activity: FragmentActivity,
private val accountId: String
) : FragmentStateAdapter(activity) {
private val fragments = MutableList<WeakReference<Fragment>?>(TAB_COUNT) { null }
override fun getItemCount() = TAB_COUNT
override fun createFragment(position: Int): Fragment {
val fragment: Fragment = when (position) {
0 -> TimelineFragment.newInstance(TimelineFragment.Kind.USER, accountId, false)
1 -> TimelineFragment.newInstance(TimelineFragment.Kind.USER_WITH_REPLIES, accountId, false)
2 -> TimelineFragment.newInstance(TimelineFragment.Kind.USER_PINNED, accountId, false)
3 -> AccountMediaFragment.newInstance(accountId, false)
else -> throw AssertionError("Page $position is out of AccountPagerAdapter bounds")
}
fragments[position] = WeakReference(fragment)
return fragment
}
fun getFragment(position: Int): Fragment? {
return fragments[position]?.get()
}
fun refreshContent() {
for (i in 0 until TAB_COUNT) {
val fragment = getFragment(i)
if (fragment != null && fragment is RefreshableFragment) {
(fragment as RefreshableFragment).refreshContent()
}
}
}
companion object {
private const val TAB_COUNT = 4
}
}

View File

@ -1,13 +1,16 @@
package com.keylesspalace.tusky.pager
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.keylesspalace.tusky.SharedElementTransitionListener
import androidx.fragment.app.FragmentActivity
import com.keylesspalace.tusky.ViewMediaAdapter
import com.keylesspalace.tusky.fragment.ViewMediaFragment
class AvatarImagePagerAdapter(fragmentManager: FragmentManager, private val avatarUrl: String) : FragmentPagerAdapter(fragmentManager), SharedElementTransitionListener {
override fun getItem(position: Int): Fragment {
class AvatarImagePagerAdapter(
activity: FragmentActivity,
private val avatarUrl: String
) : ViewMediaAdapter(activity) {
override fun createFragment(position: Int): Fragment {
return if (position == 0) {
ViewMediaFragment.newAvatarInstance(avatarUrl)
} else {
@ -15,8 +18,8 @@ class AvatarImagePagerAdapter(fragmentManager: FragmentManager, private val avat
}
}
override fun getCount() = 1
override fun getItemCount() = 1
override fun onTransitionEnd() {
override fun onTransitionEnd(position: Int) {
}
}

View File

@ -1,53 +1,42 @@
package com.keylesspalace.tusky.pager
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import com.keylesspalace.tusky.SharedElementTransitionListener
import androidx.fragment.app.FragmentActivity
import com.keylesspalace.tusky.ViewMediaAdapter
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.fragment.ViewMediaFragment
import java.util.*
import java.lang.ref.WeakReference
class ImagePagerAdapter(
fragmentManager: FragmentManager,
activity: FragmentActivity,
private val attachments: List<Attachment>,
private val initialPosition: Int
) : FragmentStatePagerAdapter(fragmentManager), SharedElementTransitionListener {
) : ViewMediaAdapter(activity) {
private var primaryItem: ViewMediaFragment? = null
private var didTransition = false
private val fragments = MutableList<WeakReference<ViewMediaFragment>?>(attachments.size) { null }
override fun setPrimaryItem(container: ViewGroup, position: Int, item: Any) {
super.setPrimaryItem(container, position, item)
this.primaryItem = item as ViewMediaFragment
}
override fun getItemCount() = attachments.size
override fun getItem(position: Int): Fragment {
return if (position >= 0 && position < attachments.size) {
override fun createFragment(position: Int): Fragment {
if (position >= 0 && position < attachments.size) {
// Fragment should not wait for or start transition if it already happened but we
// instantiate the same fragment again, e.g. open the first photo, scroll to the
// forth photo and then back to the first. The first fragment will trz to start the
// forth photo and then back to the first. The first fragment will try to start the
// transition and wait until it's over and it will never take place.
ViewMediaFragment.newInstance(
val fragment = ViewMediaFragment.newInstance(
attachment = attachments[position],
shouldStartPostponedTransition = !didTransition && position == initialPosition
)
fragments[position] = WeakReference(fragment)
return fragment
} else {
throw IllegalStateException()
}
}
override fun getCount(): Int {
return attachments.size
}
override fun getPageTitle(position: Int): CharSequence {
return String.format(Locale.getDefault(), "%d/%d", position + 1, attachments.size)
}
override fun onTransitionEnd() {
override fun onTransitionEnd(position: Int) {
this.didTransition = true
primaryItem?.onTransitionEnd()
fragments[position]?.get()?.onTransitionEnd()
}
}

View File

@ -15,49 +15,23 @@
package com.keylesspalace.tusky.pager
import android.util.SparseArray
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.viewpager.widget.PagerAdapter
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.keylesspalace.tusky.TabData
import java.lang.ref.WeakReference
class MainPagerAdapter(val tabs: List<TabData>, manager: FragmentManager) : FragmentPagerAdapter(manager) {
private val fragments = SparseArray<Fragment>(tabs.size)
class MainPagerAdapter(val tabs: List<TabData>, activity: FragmentActivity) : FragmentStateAdapter(activity) {
private val fragments = MutableList<WeakReference<Fragment>?>(tabs.size) { null }
override fun getItem(position: Int): Fragment {
override fun createFragment(position: Int): Fragment {
val tab = tabs[position]
return tab.fragment(tab.arguments)
}
override fun getCount(): Int {
return tabs.size
}
override fun getPageTitle(position: Int): CharSequence? {
return null
}
override fun getItemId(position: Int): Long {
return tabs[position].hashCode() + position.toLong()
}
override fun getItemPosition(item: Any): Int {
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)
val fragment = tab.fragment(tab.arguments)
fragments[position] = WeakReference(fragment)
return fragment
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
super.destroyItem(container, position, `object`)
fragments.remove(position)
}
override fun getItemCount() = tabs.size
fun getFragment(position: Int): Fragment? = fragments[position]
fun getFragment(position: Int): Fragment? = fragments[position]?.get()
}

View File

@ -1,45 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.view;
import android.content.Context;
import androidx.viewpager.widget.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* This class is entirely to address a known issue with com.github.chrisbanes.photoview.PhotoView.
* ViewPager will throw exceptions when a PhotoView is placed within it, so this subclass eats those
* exceptions.
*/
public class ImageViewPager extends ViewPager {
public ImageViewPager(Context context) {
super(context);
}
public ImageViewPager(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
return false;
}
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tab_page_margin_black" />
</shape>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tab_page_margin_dark" />
</shape>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tab_page_margin_light" />
</shape>

View File

@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/tab_page_margin_drawable">
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="640dp"
@ -45,16 +44,17 @@
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/elephant_error"
tools:visibility="visible" />
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/topProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
app:layout_constraintTop_toTopOf="parent"
android:indeterminate="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="parent"
android:visibility="gone"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/tab_page_margin_drawable">
android:layout_height="match_parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="640dp"
@ -80,9 +79,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@android:color/transparent"
android:visibility="gone"
tools:src="@drawable/elephant_error"
tools:visibility="visible" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/tab_page_margin_drawable">
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"

View File

@ -333,10 +333,11 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/accountFragmentViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/tab_page_margin_color"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.floatingactionbutton.FloatingActionButton

View File

@ -33,8 +33,9 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:background="?attr/tab_page_margin_color"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/tab_layout"

View File

@ -32,7 +32,7 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pages"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@ -6,7 +6,7 @@
android:background="@android:color/black"
android:orientation="vertical">
<com.keylesspalace.tusky.view.ImageViewPager
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -4,20 +4,20 @@
android:id="@+id/layoutRoot"
android:layout_width="@dimen/timeline_width"
android:layout_height="match_parent"
android:background="?attr/tab_page_margin_drawable">
android:background="?attr/tab_page_margin_color">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/searchRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="?attr/window_background"
tools:listitem="@layout/item_account"/>
android:id="@+id/searchRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="?attr/window_background"
tools:listitem="@layout/item_account" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@ -3,7 +3,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="?attr/window_background">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
@ -29,23 +31,22 @@
android:id="@+id/statusView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:color/transparent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/elephant_error"
tools:visibility="visible"/>
tools:visibility="visible" />
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/topProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
app:layout_constraintTop_toTopOf="parent"
android:indeterminate="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="parent"
android:visibility="gone"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -58,7 +58,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
android:background="?attr/window_background" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@ -9,5 +9,7 @@
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/window_background"
android:scrollbars="vertical" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@ -41,7 +41,7 @@
<item name="android:listDivider">@drawable/status_divider_dark</item>
<item name="conversation_thread_line_drawable">@drawable/conversation_thread_line_dark</item>
<item name="tab_icon_selected_tint">@color/tusky_blue</item>
<item name="tab_page_margin_drawable">@drawable/tab_page_margin_dark</item>
<item name="tab_page_margin_color">@color/tab_page_margin_dark</item>
<item name="account_header_background_color">@color/color_background_dark</item>
<item name="account_toolbar_icon_tint_uncollapsed">@color/toolbar_icon_dark</item>
<item name="account_toolbar_icon_tint_collapsed">@color/account_toolbar_icon_collapsed_dark</item>

View File

@ -25,7 +25,7 @@
<attr name="media_preview_unloaded_drawable" format="reference" />
<attr name="conversation_thread_line_drawable" format="reference" />
<attr name="tab_icon_selected_tint" format="reference|color" />
<attr name="tab_page_margin_drawable" format="reference" />
<attr name="tab_page_margin_color" format="reference|color" />
<attr name="account_header_background_color" format="reference|color" />
<attr name="account_toolbar_icon_tint_uncollapsed" format="reference|color" />
<attr name="account_toolbar_icon_tint_collapsed" format="reference|color" />

View File

@ -96,7 +96,7 @@
<item name="conversation_thread_line_drawable">@drawable/conversation_thread_line_light
</item>
<item name="tab_icon_selected_tint">@color/tusky_blue</item>
<item name="tab_page_margin_drawable">@drawable/tab_page_margin_light</item>
<item name="tab_page_margin_color">@color/tab_page_margin_light</item>
<item name="account_header_background_color">@color/color_primary_dark_light</item>
<item name="account_toolbar_icon_tint_uncollapsed">@color/toolbar_icon_dark
</item> <!--Default to dark on purpose, because header backgrounds with gradients are always dark.-->
@ -200,7 +200,7 @@
<item name="material_drawer_selected">@color/color_primary_black</item>
<item name="material_drawer_selected_text">@color/text_color_primary_black</item>
<item name="material_drawer_header_selection_text">@color/text_color_primary_black</item>
<item name="tab_page_margin_drawable">@drawable/tab_page_margin_black</item>
<item name="tab_page_margin_color">@color/tab_page_margin_black</item>
</style>
<style name="TuskyBlackTheme" parent="TuskyBlackThemeBase" />