From 48bceefea297f2fb333f21eb18db40ea4847726e Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 11 Jun 2024 16:28:42 +0200 Subject: [PATCH 1/5] Fix java.lang.IllegalStateException: Design assumption violated. Signed-off-by: alperozturk --- .../com/owncloud/android/ui/preview/PreviewImageFragment.java | 4 +--- .../owncloud/android/ui/preview/PreviewImagePagerAdapter.kt | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java index 1ca7f2dfae..417f9f060d 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -79,9 +79,7 @@ import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentTransaction; -import androidx.viewpager2.adapter.FragmentStateAdapter; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import pl.droidsonroids.gif.GifDrawable; @@ -128,7 +126,7 @@ public class PreviewImageFragment extends FileFragment implements Injectable { * This method hides to client objects the need of doing the construction in two steps. * * @param imageFile An {@link OCFile} to preview as an image in the fragment - * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStateAdapter} ; + * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of { FragmentStateAdapter } ; * TODO better solution */ public static PreviewImageFragment newInstance(@NonNull OCFile imageFile, diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt index 0132567a02..38f350999b 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.kt @@ -135,10 +135,6 @@ class PreviewImagePagerAdapter : FragmentStateAdapter { } } - override fun getItemId(position: Int): Long { - return imageFiles[position].hashCode().toLong() - } - private fun addVideoOfLivePhoto(file: OCFile) { file.livePhotoVideo = selectedFile } From 5648f31af449f3a6b521deeeb2fc84dd0eb5b2b2 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 11 Jun 2024 16:46:22 +0200 Subject: [PATCH 2/5] Migrate ViewPager 1 to ViewPager 2 Signed-off-by: alperozturk --- .../client/onboarding/FirstRunActivity.kt | 26 ++++------ .../client/onboarding/WhatsNewActivity.kt | 24 +++++----- .../ui/adapter/FeaturesViewAdapter.java | 18 +++---- .../ui/adapter/FeaturesWebViewAdapter.java | 16 ++++--- .../ui/adapter/FileDetailTabAdapter.java | 48 ++++++++++--------- .../ui/fragment/FileDetailFragment.java | 19 ++++++-- .../main/res/layout/file_details_fragment.xml | 2 +- .../main/res/layout/first_run_activity.xml | 2 +- .../main/res/layout/whats_new_activity.xml | 2 +- 9 files changed, 82 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt index d919ed52af..a266669705 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.kt @@ -18,7 +18,7 @@ import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2 import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.appinfo.AppInfo @@ -39,7 +39,7 @@ import javax.inject.Inject /** * Activity displaying general feature after a fresh install. */ -class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injectable { +class FirstRunActivity : BaseActivity(), Injectable { @JvmField @Inject @@ -171,10 +171,14 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta @Suppress("SpreadOperator") private fun setupFeaturesViewAdapter() { - val featuresViewAdapter = FeaturesViewAdapter(supportFragmentManager, *firstRun) - binding.progressIndicator.setNumberOfSteps(featuresViewAdapter.count) + val featuresViewAdapter = FeaturesViewAdapter(this, *firstRun) + binding.progressIndicator.setNumberOfSteps(featuresViewAdapter.itemCount) binding.contentPanel.adapter = featuresViewAdapter - binding.contentPanel.addOnPageChangeListener(this) + binding.contentPanel.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + binding.progressIndicator.animateToStep(position + 1) + } + }) } private fun handleOnBackPressed() { @@ -236,18 +240,6 @@ class FirstRunActivity : BaseActivity(), ViewPager.OnPageChangeListener, Injecta super.onStop() } - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { - // unused but to be implemented due to abstract parent - } - - override fun onPageSelected(position: Int) { - binding.progressIndicator.animateToStep(position + 1) - } - - override fun onPageScrollStateChanged(state: Int) { - // unused but to be implemented due to abstract parent - } - companion object { const val EXTRA_ALLOW_CLOSE = "ALLOW_CLOSE" const val EXTRA_EXIT = "EXIT" diff --git a/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt b/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt index 669587658b..b1fdf9e234 100644 --- a/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt +++ b/app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt @@ -13,7 +13,7 @@ import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback import androidx.fragment.app.FragmentActivity -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.appinfo.AppInfo import com.nextcloud.client.di.Injectable @@ -29,7 +29,7 @@ import javax.inject.Inject /** * Activity displaying new features after an update. */ -class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Injectable { +class WhatsNewActivity : FragmentActivity(), Injectable { @JvmField @Inject @@ -64,7 +64,11 @@ class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Inj val showWebView = urls.isNotEmpty() setupFeatureViewAdapter(showWebView, urls) - binding.contentPanel.addOnPageChangeListener(this) + binding.contentPanel.registerOnPageChangeCallback(object : OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + controlPanelOnPageSelected(position) + } + }) setupForwardImageButton() setupSkipImageButton() setupWelcomeText(showWebView) @@ -75,15 +79,15 @@ class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Inj @Suppress("SpreadOperator") private fun setupFeatureViewAdapter(showWebView: Boolean, urls: Array) { val adapter = if (showWebView) { - FeaturesWebViewAdapter(supportFragmentManager, *urls) + FeaturesWebViewAdapter(this, *urls) } else { onboarding?.let { - FeaturesViewAdapter(supportFragmentManager, *it.whatsNew) + FeaturesViewAdapter(this, *it.whatsNew) } } adapter?.let { - binding.progressIndicator.setNumberOfSteps(it.count) + binding.progressIndicator.setNumberOfSteps(it.itemCount) binding.contentPanel.adapter = it } } @@ -142,14 +146,8 @@ class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Inj preferences?.lastSeenVersionCode = BuildConfig.VERSION_CODE } - override fun onPageSelected(position: Int) { + private fun controlPanelOnPageSelected(position: Int) { binding.progressIndicator.animateToStep(position + 1) updateNextButtonIfNeeded() } - - @Suppress("EmptyFunctionBlock") - override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} - - @Suppress("EmptyFunctionBlock") - override fun onPageScrollStateChanged(state: Int) {} } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesViewAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesViewAdapter.java index 2731d9fc88..f4536999af 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesViewAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesViewAdapter.java @@ -9,26 +9,28 @@ package com.owncloud.android.ui.adapter; import com.owncloud.android.features.FeatureItem; import com.owncloud.android.ui.fragment.FeatureFragment; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; -public class FeaturesViewAdapter extends FragmentPagerAdapter { +public class FeaturesViewAdapter extends FragmentStateAdapter { - private FeatureItem[] mFeatures; + private final FeatureItem[] mFeatures; - public FeaturesViewAdapter(FragmentManager fm, FeatureItem... features) { - super(fm); + public FeaturesViewAdapter(FragmentActivity fragmentActivity, FeatureItem... features) { + super(fragmentActivity); mFeatures = features; } + @NonNull @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { return FeatureFragment.newInstance(mFeatures[position]); } @Override - public int getCount() { + public int getItemCount() { return mFeatures.length; } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesWebViewAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesWebViewAdapter.java index 1cf0c90759..2de789b24c 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesWebViewAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/FeaturesWebViewAdapter.java @@ -8,25 +8,27 @@ package com.owncloud.android.ui.adapter; import com.owncloud.android.ui.fragment.FeatureWebFragment; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; -public class FeaturesWebViewAdapter extends FragmentPagerAdapter { +public class FeaturesWebViewAdapter extends FragmentStateAdapter { private String[] mWebUrls; - public FeaturesWebViewAdapter(FragmentManager fm, String... webUrls) { - super(fm); + public FeaturesWebViewAdapter(FragmentActivity fragmentActivity, String... webUrls) { + super(fragmentActivity); mWebUrls = webUrls; } + @NonNull @Override - public Fragment getItem(int position) { + public Fragment createFragment(int position) { return FeatureWebFragment.newInstance(mWebUrls[position]); } @Override - public int getCount() { + public int getItemCount() { return mWebUrls.length; } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java index 0e53c24745..3c20828d19 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java @@ -15,13 +15,13 @@ import com.owncloud.android.utils.MimeTypeUtil; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; /** * File details pager adapter. */ -public class FileDetailTabAdapter extends FragmentStatePagerAdapter { +public class FileDetailTabAdapter extends FragmentStateAdapter { private final OCFile file; private final User user; private final boolean showSharingTab; @@ -30,33 +30,16 @@ public class FileDetailTabAdapter extends FragmentStatePagerAdapter { private FileDetailActivitiesFragment fileDetailActivitiesFragment; private ImageDetailFragment imageDetailFragment; - public FileDetailTabAdapter(FragmentManager fm, + public FileDetailTabAdapter(FragmentActivity fragmentActivity, OCFile file, User user, boolean showSharingTab) { - super(fm); + super(fragmentActivity); this.file = file; this.user = user; this.showSharingTab = showSharingTab; } - @NonNull - @Override - public Fragment getItem(int position) { - switch (position) { - case 0: - default: - fileDetailActivitiesFragment = FileDetailActivitiesFragment.newInstance(file, user); - return fileDetailActivitiesFragment; - case 1: - fileDetailSharingFragment = FileDetailSharingFragment.newInstance(file, user); - return fileDetailSharingFragment; - case 2: - imageDetailFragment = ImageDetailFragment.newInstance(file, user); - return imageDetailFragment; - } - } - public FileDetailSharingFragment getFileDetailSharingFragment() { return fileDetailSharingFragment; } @@ -69,8 +52,27 @@ public class FileDetailTabAdapter extends FragmentStatePagerAdapter { return imageDetailFragment; } + @NonNull @Override - public int getCount() { + public Fragment createFragment(int position) { + return switch (position) { + default -> { + fileDetailActivitiesFragment = FileDetailActivitiesFragment.newInstance(file, user); + yield fileDetailActivitiesFragment; + } + case 1 -> { + fileDetailSharingFragment = FileDetailSharingFragment.newInstance(file, user); + yield fileDetailSharingFragment; + } + case 2 -> { + imageDetailFragment = ImageDetailFragment.newInstance(file, user); + yield imageDetailFragment; + } + }; + } + + @Override + public int getItemCount() { if (showSharingTab) { if (MimeTypeUtil.isImage(file)) { return 3; diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index 3e76c8d1df..ef2d46820b 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -75,6 +75,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.FragmentManager; +import androidx.viewpager2.widget.ViewPager2; /** * This Fragment is used to display the details about a file. @@ -166,7 +167,12 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, if (binding == null) { return null; } - return ((FileDetailTabAdapter) binding.pager.getAdapter()).getFileDetailSharingFragment(); + + if (binding.pager.getAdapter() instanceof FileDetailTabAdapter adapter) { + return adapter.getFileDetailSharingFragment(); + } + + return null; } /** @@ -175,7 +181,11 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, * @return reference to the {@link FileDetailActivitiesFragment} */ public FileDetailActivitiesFragment getFileDetailActivitiesFragment() { - return ((FileDetailTabAdapter) binding.pager.getAdapter()).getFileDetailActivitiesFragment(); + if (binding.pager.getAdapter() instanceof FileDetailTabAdapter adapter) { + return adapter.getFileDetailActivitiesFragment(); + } + + return null; } public void goBackToOCFileListFragment() { @@ -296,12 +306,13 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, viewThemeUtils.material.themeTabLayout(binding.tabLayout); - final FileDetailTabAdapter adapter = new FileDetailTabAdapter(getFragmentManager(), + final FileDetailTabAdapter adapter = new FileDetailTabAdapter(requireActivity(), getFile(), user, showSharingTab()); binding.pager.setAdapter(adapter); - binding.pager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.tabLayout) { + + binding.pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { final FileDetailActivitiesFragment fragment = getFileDetailActivitiesFragment(); diff --git a/app/src/main/res/layout/file_details_fragment.xml b/app/src/main/res/layout/file_details_fragment.xml index 8efdd44785..c00d53999a 100644 --- a/app/src/main/res/layout/file_details_fragment.xml +++ b/app/src/main/res/layout/file_details_fragment.xml @@ -185,7 +185,7 @@ app:tabTextColor="@color/text_color" app:tabInlineLabel="true" /> - diff --git a/app/src/main/res/layout/first_run_activity.xml b/app/src/main/res/layout/first_run_activity.xml index bff1c49a4b..a9a44b6922 100644 --- a/app/src/main/res/layout/first_run_activity.xml +++ b/app/src/main/res/layout/first_run_activity.xml @@ -20,7 +20,7 @@ android:layout_marginBottom="@dimen/standard_margin" android:layout_marginTop="@dimen/standard_margin"> - - Date: Tue, 11 Jun 2024 16:48:55 +0200 Subject: [PATCH 3/5] Remove ViewPager 1 from dependencies Signed-off-by: alperozturk --- gradle/verification-metadata.xml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index ca062cae51..456e235d7d 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5805,17 +5805,14 @@ - - - - - - - - + + + + + + + + From a8215488e0f4c0f13d04025a9a8dd56ca1dd22ef Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 12 Jun 2024 11:01:26 +0200 Subject: [PATCH 4/5] Fix ss test Signed-off-by: alperozturk --- .../ui/fragment/FileDetailFragmentStaticServerIT.kt | 11 ++++++----- .../android/ui/fragment/FileDetailFragment.java | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index 12193a181a..fbc88e8196 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -9,6 +9,7 @@ package com.owncloud.android.ui.fragment import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.platform.app.InstrumentationRegistry import com.nextcloud.test.TestActivity import com.nextcloud.ui.ImageDetailFragment import com.owncloud.android.AbstractIT @@ -189,10 +190,10 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { val sut = testActivityRule.launchActivity(null) sut.addFragment(FileDetailFragment.newInstance(oCFile, user, 1)) - waitForIdleSync() - - shortSleep() - shortSleep() - screenshot(sut) + InstrumentationRegistry.getInstrumentation().waitForIdle { + shortSleep() + shortSleep() + screenshot(sut) + } } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index ef2d46820b..048b26b6cf 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -345,10 +345,11 @@ public class FileDetailFragment extends FileFragment implements OnClickListener, } }); - TabLayout.Tab tab = binding.tabLayout.getTabAt(activeTab); - if (tab != null) { - tab.select(); - } + binding.tabLayout.post(() -> { + TabLayout.Tab tab1 = binding.tabLayout.getTabAt(activeTab); + if (tab1 == null) return; + tab1.select(); + }); } @Override From 20d5423ccf83b1a8b0297fb76491e75a8a51f097 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 12 Jun 2024 11:41:49 +0200 Subject: [PATCH 5/5] Fix ss test Signed-off-by: alperozturk --- .../ui/fragment/FileDetailFragmentStaticServerIT.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt index fbc88e8196..12193a181a 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailFragmentStaticServerIT.kt @@ -9,7 +9,6 @@ package com.owncloud.android.ui.fragment import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.platform.app.InstrumentationRegistry import com.nextcloud.test.TestActivity import com.nextcloud.ui.ImageDetailFragment import com.owncloud.android.AbstractIT @@ -190,10 +189,10 @@ class FileDetailFragmentStaticServerIT : AbstractIT() { val sut = testActivityRule.launchActivity(null) sut.addFragment(FileDetailFragment.newInstance(oCFile, user, 1)) - InstrumentationRegistry.getInstrumentation().waitForIdle { - shortSleep() - shortSleep() - screenshot(sut) - } + waitForIdleSync() + + shortSleep() + shortSleep() + screenshot(sut) } }