From 12312393b28c3b8b6f60ef49e870d86aa9175585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Brey?= Date: Thu, 5 May 2022 11:03:00 +0200 Subject: [PATCH 1/3] Replace fastscroll library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Smoother scrolling Signed-off-by: Álvaro Brey --- app/build.gradle | 2 +- .../com/nextcloud/utils/view/FastScroll.kt | 38 +++++++++++++++++++ .../android/ui/EmptyRecyclerView.java | 5 +-- .../android/ui/adapter/GalleryAdapter.kt | 6 +-- .../android/ui/adapter/OCFileListAdapter.java | 7 ++-- .../android/ui/fragment/GalleryFragment.java | 17 ++------- .../ui/fragment/OCFileListFragment.java | 3 ++ app/src/main/res/layout/list_fragment.xml | 7 +--- 8 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/view/FastScroll.kt diff --git a/app/build.gradle b/app/build.gradle index 721e6d4090..d40d16b01d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -272,7 +272,7 @@ dependencies { implementation "com.google.android.exoplayer:exoplayer:$exoplayerVersion" implementation "com.google.android.exoplayer:extension-okhttp:$exoplayerVersion" - implementation 'com.simplecityapps:recyclerview-fastscroll:2.0.1' + implementation 'me.zhanghai.android.fastscroll:library:1.1.8' // Shimmer animation implementation 'io.github.elye:loaderviewlibrary:3.0.0' diff --git a/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt b/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt new file mode 100644 index 0000000000..b50e76ece6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Android Library is available under MIT license + * + * @author Álvaro Brey Vilas + * Copyright (C) 2022 Álvaro Brey Vilas + * Copyright (C) 2022 Nextcloud GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.nextcloud.utils.view + +import androidx.recyclerview.widget.RecyclerView +import me.zhanghai.android.fastscroll.FastScrollerBuilder + +object FastScroll { + @JvmStatic + fun applyFastScroll(rv: RecyclerView) { + FastScrollerBuilder(rv).useMd2Style().build() + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java b/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java index b45b5355c5..562e0a7796 100644 --- a/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java +++ b/app/src/main/java/com/owncloud/android/ui/EmptyRecyclerView.java @@ -25,14 +25,13 @@ import android.content.Context; import android.util.AttributeSet; import android.view.View; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; - import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; /** * Extends RecyclerView to show a custom view if no data is available Inspired by http://alexzh.com/tutorials/how-to-setemptyview-to-recyclerview */ -public class EmptyRecyclerView extends FastScrollRecyclerView { +public class EmptyRecyclerView extends RecyclerView { private View mEmptyView; private boolean hasFooter = false; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt index ee6ae05dd5..7a207e7502 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt @@ -45,7 +45,7 @@ import com.owncloud.android.utils.FileSortOrder import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.theme.ThemeColorUtils import com.owncloud.android.utils.theme.ThemeDrawableUtils -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.SectionedAdapter +import me.zhanghai.android.fastscroll.PopupTextProvider import java.util.Calendar import java.util.Date @@ -58,7 +58,7 @@ class GalleryAdapter( transferServiceGetter: ComponentsGetter, themeColorUtils: ThemeColorUtils, themeDrawableUtils: ThemeDrawableUtils -) : SectionedRecyclerViewAdapter(), CommonOCFileListAdapterInterface, SectionedAdapter { +) : SectionedRecyclerViewAdapter(), CommonOCFileListAdapterInterface, PopupTextProvider { private var files: List = mutableListOf() private val ocFileListDelegate: OCFileListDelegate private var storageManager: FileDataStorageManager @@ -122,7 +122,7 @@ class GalleryAdapter( return files.size } - override fun getSectionName(position: Int): String { + override fun getPopupText(position: Int): String { return DisplayUtils.getDateByPattern( files[getRelativePosition(position).section()].date, context, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 70c91d9512..a8a32bb2e5 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -74,7 +74,6 @@ import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ThemeAvatarUtils; import com.owncloud.android.utils.theme.ThemeColorUtils; import com.owncloud.android.utils.theme.ThemeDrawableUtils; -import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.io.File; import java.text.SimpleDateFormat; @@ -90,14 +89,14 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.recyclerview.widget.RecyclerView; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import me.zhanghai.android.fastscroll.PopupTextProvider; /** * This Adapter populates a RecyclerView with all files and folders in a Nextcloud instance. */ public class OCFileListAdapter extends RecyclerView.Adapter implements DisplayUtils.AvatarGenerationListener, - CommonOCFileListAdapterInterface, - FastScrollRecyclerView.SectionedAdapter { + CommonOCFileListAdapterInterface, PopupTextProvider { private static final int showFilenameColumnThreshold = 4; private final String userId; @@ -909,7 +908,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter + android:layout_height="match_parent" /> Date: Thu, 5 May 2022 14:37:32 +0200 Subject: [PATCH 2/3] Fix fast scroll in gallery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gallery has a grid layout + variable row height (due to section titles) This is a no-no in all fastscroll libraries. However, the new one supports writing custom logic to handle this, which is what this commit is. Signed-off-by: Álvaro Brey --- .../com/nextcloud/utils/view/FastScroll.kt | 10 +- .../android/ui/adapter/GalleryAdapter.kt | 2 +- .../android/ui/fragment/GalleryFragment.java | 3 +- .../util/GalleryFastScrollViewHelper.kt | 264 ++++++++++++++++++ 4 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/fragment/util/GalleryFastScrollViewHelper.kt diff --git a/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt b/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt index b50e76ece6..1eecdaec16 100644 --- a/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt +++ b/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt @@ -28,11 +28,17 @@ package com.nextcloud.utils.view import androidx.recyclerview.widget.RecyclerView +import me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScrollerBuilder object FastScroll { @JvmStatic - fun applyFastScroll(rv: RecyclerView) { - FastScrollerBuilder(rv).useMd2Style().build() + @JvmOverloads + fun applyFastScroll(recyclerView: RecyclerView, viewHelper: FastScroller.ViewHelper? = null) { + val builder = FastScrollerBuilder(recyclerView).useMd2Style() + if (viewHelper != null) { + builder.setViewHelper(viewHelper) + } + builder.build() } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt index 7a207e7502..77381fe97a 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt @@ -59,7 +59,7 @@ class GalleryAdapter( themeColorUtils: ThemeColorUtils, themeDrawableUtils: ThemeDrawableUtils ) : SectionedRecyclerViewAdapter(), CommonOCFileListAdapterInterface, PopupTextProvider { - private var files: List = mutableListOf() + var files: List = mutableListOf() private val ocFileListDelegate: OCFileListDelegate private var storageManager: FileDataStorageManager diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java index aa54fb2340..bc08421734 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java @@ -36,6 +36,7 @@ import com.owncloud.android.ui.adapter.CommonOCFileListAdapterInterface; import com.owncloud.android.ui.adapter.GalleryAdapter; import com.owncloud.android.ui.asynctasks.GallerySearchTask; import com.owncloud.android.ui.events.ChangeMenuEvent; +import com.owncloud.android.ui.fragment.util.GalleryFastScrollViewHelper; import androidx.annotation.NonNull; import androidx.fragment.app.FragmentActivity; @@ -117,7 +118,7 @@ public class GalleryFragment extends OCFileListFragment { mAdapter.setLayoutManager(layoutManager); getRecyclerView().setLayoutManager(layoutManager); - FastScroll.applyFastScroll(getRecyclerView()); + FastScroll.applyFastScroll(getRecyclerView(), new GalleryFastScrollViewHelper(getRecyclerView(), mAdapter)); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/util/GalleryFastScrollViewHelper.kt b/app/src/main/java/com/owncloud/android/ui/fragment/util/GalleryFastScrollViewHelper.kt new file mode 100644 index 0000000000..52a2d725be --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/fragment/util/GalleryFastScrollViewHelper.kt @@ -0,0 +1,264 @@ +/* + * Nextcloud Android Library is available under MIT license + * + * @author Álvaro Brey Vilas + * Copyright (C) 2022 Álvaro Brey Vilas + * Copyright (C) 2022 Nextcloud GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.owncloud.android.ui.fragment.util + +import android.graphics.Canvas +import android.graphics.Rect +import android.view.MotionEvent +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ItemDecoration +import androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener +import com.afollestad.sectionedrecyclerview.ItemCoord +import com.owncloud.android.datamodel.GalleryItems +import com.owncloud.android.ui.adapter.GalleryAdapter +import me.zhanghai.android.fastscroll.FastScroller +import me.zhanghai.android.fastscroll.PopupTextProvider +import me.zhanghai.android.fastscroll.Predicate +import kotlin.math.ceil + +/** + * Custom ViewHelper to get fast scroll working on gallery, which has a gridview and variable height (due to headers) + * + * Copied from me.zhanghai.android.fastscroll.RecyclerViewHelper and heavily modified for gallery structure + */ +class GalleryFastScrollViewHelper( + private val mView: RecyclerView, + private val mPopupTextProvider: PopupTextProvider? +) : FastScroller.ViewHelper { + // used to calculate paddings + private val mTempRect = Rect() + + private val layoutManager by lazy { mView.layoutManager as GridLayoutManager } + + // header is always 1st in the adapter + private val headerHeight by lazy { getItemHeight(0) } + // the 2nd element is always an item + private val rowHeight by lazy { getItemHeight(1) } + + private val columnCount by lazy { layoutManager.spanCount } + + private fun getItemHeight(position: Int): Int { + if (mView.childCount <= position) { + return 0 + } + val itemView = mView.getChildAt(position) + mView.getDecoratedBoundsWithMargins(itemView, mTempRect) + return mTempRect.height() + } + + override fun addOnPreDrawListener(onPreDraw: Runnable) { + mView.addItemDecoration(object : ItemDecoration() { + override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { + onPreDraw.run() + } + }) + } + + override fun addOnScrollChangedListener(onScrollChanged: Runnable) { + mView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + onScrollChanged.run() + } + }) + } + + override fun addOnTouchEventListener(onTouchEvent: Predicate) { + mView.addOnItemTouchListener(object : SimpleOnItemTouchListener() { + override fun onInterceptTouchEvent(recyclerView: RecyclerView, event: MotionEvent): Boolean { + return onTouchEvent.test(event) + } + + override fun onTouchEvent(recyclerView: RecyclerView, event: MotionEvent) { + onTouchEvent.test(event) + } + }) + } + + override fun getScrollRange(): Int { + val headerCount = getHeaderCount() + val rowCount = getRowCount() + + if (headerCount == 0 || rowCount == 0) { + return 0 + } + val totalHeaderHeight = headerCount * headerHeight + val totalRowHeight = rowCount * rowHeight + return mView.paddingTop + totalHeaderHeight + totalRowHeight + mView.paddingBottom + } + + private fun getHeaderCount(): Int { + val adapter = mView.adapter as GalleryAdapter + return adapter.sectionCount + } + + private fun getRowCount(): Int { + val adapter = mView.adapter as GalleryAdapter + if (adapter.sectionCount == 0) return 0 + // in each section, the final row may contain less than the max of items + return adapter.files.sumOf { itemCountToRowCount(it.files.size) } + } + + /** + * Calculates current absolute offset depending on view state (first visible element) + */ + override fun getScrollOffset(): Int { + val firstItemPosition = getFirstItemAdapterPosition() + if (firstItemPosition == RecyclerView.NO_POSITION) { + return 0 + } + + val adapter = mView.adapter as GalleryAdapter + val itemCoord: ItemCoord = adapter.getRelativePosition(firstItemPosition) + val isHeader = itemCoord.relativePos() == -1 + + val seenRowsInPreviousSections = adapter.files + .subList(0, itemCoord.section()) + .sumOf { itemCountToRowCount(it.files.size) } + val seenRowsInThisSection = if (isHeader) 0 else itemCountToRowCount(itemCoord.relativePos()) + val totalSeenRows = seenRowsInPreviousSections + seenRowsInThisSection + + val seenHeaders = when { + isHeader -> itemCoord.section() // don't count the current section header + else -> itemCoord.section() + 1 + } + + val firstItemTop = getFirstItemOffset() + + val totalRowOffset = totalSeenRows * rowHeight + val totalHeaderOffset = seenHeaders * headerHeight + return mView.paddingTop + totalHeaderOffset + totalRowOffset - firstItemTop + } + + /** + * Scrolls to an absolute offset + */ + override fun scrollTo(offset: Int) { + mView.stopScroll() + val offsetTmp = offset - mView.paddingTop + val (position, remainingOffset) = findPositionForOffset(offsetTmp) + scrollToPositionWithOffset(position, -remainingOffset) + } + + /** + * Given an absolute offset, returns the closest position to that offset (without going over it), + * and the remaining offset + */ + private fun findPositionForOffset(offset: Int): Pair { + val adapter = mView.adapter as GalleryAdapter + + // find section + val sectionStartOffsets = getSectionStartOffsets(adapter.files) + val previousSections = sectionStartOffsets.filter { it <= offset } + + val section = previousSections.size - 1 + val sectionStartOffset = previousSections.last() + + // now calculate where to scroll within the section + var remainingOffset = offset - sectionStartOffset + val positionWithinSection: Int + if (remainingOffset <= headerHeight) { + // header position + positionWithinSection = -1 + } else { + // row position + remainingOffset -= headerHeight + val rowCount = remainingOffset / rowHeight + if (rowCount > 0) { + val rowStartIndex = rowCount * columnCount + positionWithinSection = rowStartIndex + + remainingOffset -= rowCount * rowHeight + } else { + positionWithinSection = 0 // first item + } + } + val absolutePosition = adapter.getAbsolutePosition(section, positionWithinSection) + return Pair(absolutePosition, remainingOffset) + } + + /** + * Returns a list of the offset heights at which the section corresponding to that index starts + */ + private fun getSectionStartOffsets(files: List): List { + val sectionHeights = + files.map { headerHeight + itemCountToRowCount(it.files.size) * rowHeight } + val sectionStartOffsets = sectionHeights.indices.map { i -> + when (i) { + 0 -> 0 + else -> sectionHeights.subList(0, i).sum() + } + } + return sectionStartOffsets + } + + private fun itemCountToRowCount(itemsCount: Int): Int { + return ceil(itemsCount.toDouble() / columnCount).toInt() + } + + override fun getPopupText(): String? { + var popupTextProvider: PopupTextProvider? = mPopupTextProvider + if (popupTextProvider == null) { + val adapter = mView.adapter + if (adapter is PopupTextProvider) { + popupTextProvider = adapter + } + } + if (popupTextProvider == null) { + return null + } + val position = getFirstItemAdapterPosition() + return if (position == RecyclerView.NO_POSITION) { + null + } else popupTextProvider.getPopupText(position) + } + + private fun getFirstItemAdapterPosition(): Int { + if (mView.childCount == 0) { + return RecyclerView.NO_POSITION + } + val itemView = mView.getChildAt(0) + return layoutManager.getPosition(itemView) + } + + private fun getFirstItemOffset(): Int { + if (mView.childCount == 0) { + return RecyclerView.NO_POSITION + } + val itemView = mView.getChildAt(0) + mView.getDecoratedBoundsWithMargins(itemView, mTempRect) + return mTempRect.top + } + + private fun scrollToPositionWithOffset(position: Int, offset: Int) { + var offsetTmp = offset + // LinearLayoutManager actually takes offset from paddingTop instead of top of RecyclerView. + offsetTmp -= mView.paddingTop + layoutManager.scrollToPositionWithOffset(position, offsetTmp) + } +} From ea4222a1ae326ec97f6d4162978d0ffa6db1cbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Brey?= Date: Thu, 5 May 2022 15:12:14 +0200 Subject: [PATCH 3/3] FileDisplayActivity: fix appbar for fast scroll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds padding at the bottom if appbar is not collapsed Signed-off-by: Álvaro Brey --- .../java/com/nextcloud/utils/view/FastScroll.kt | 17 +++++++++++++++++ .../ui/activity/FileDisplayActivity.java | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt b/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt index 1eecdaec16..2d268a5f61 100644 --- a/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt +++ b/app/src/main/java/com/nextcloud/utils/view/FastScroll.kt @@ -27,7 +27,9 @@ package com.nextcloud.utils.view +import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.appbar.AppBarLayout import me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScrollerBuilder @@ -41,4 +43,19 @@ object FastScroll { } builder.build() } + + @JvmStatic + fun fixAppBarForFastScroll(appBarLayout: AppBarLayout, content: ViewGroup) { + val contentLayoutInitialPaddingBottom = content.paddingBottom + appBarLayout.addOnOffsetChangedListener( + AppBarLayout.OnOffsetChangedListener { _, offset -> + content.setPadding( + content.paddingLeft, + content.paddingTop, + content.paddingRight, + contentLayoutInitialPaddingBottom + appBarLayout.totalScrollRange + offset + ) + } + ) + } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 2913cf2020..0bb1e030b7 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -61,6 +61,7 @@ import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.utils.IntentUtil; import com.nextcloud.java.util.Optional; +import com.nextcloud.utils.view.FastScroll; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.FilesBinding; @@ -137,7 +138,6 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SearchView; -import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.MenuItemCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -266,6 +266,9 @@ public class FileDisplayActivity extends FileActivity mSwitchAccountButton.setOnClickListener(v -> showManageAccountsDialog()); + FastScroll.fixAppBarForFastScroll(binding.appbar.appbar, binding.rootLayout); + + // Init Fragment without UI to retain AsyncTask across configuration changes FragmentManager fm = getSupportFragmentManager(); TaskRetainerFragment taskRetainerFragment =