소스 검색

Merge pull request #13093 from nextcloud/view-pager-2-implementation

Migrate to View Pager 2 & Crash-Fix | Farewell to ViewPager 1 🥳
Alper Öztürk 1 년 전
부모
커밋
5d83a2961b

+ 9 - 17
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"

+ 11 - 13
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<String>) {
         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) {}
 }

+ 10 - 8
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;
     }
 }

+ 9 - 7
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;
     }
 }

+ 25 - 23
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 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 getCount() {
+    public int getItemCount() {
         if (showSharingTab) {
             if (MimeTypeUtil.isImage(file)) {
                 return 3;

+ 20 - 8
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();
@@ -334,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

+ 1 - 3
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,

+ 0 - 4
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
     }

+ 1 - 1
app/src/main/res/layout/file_details_fragment.xml

@@ -185,7 +185,7 @@
         app:tabTextColor="@color/text_color"
         app:tabInlineLabel="true" />
 
-    <androidx.viewpager.widget.ViewPager
+    <androidx.viewpager2.widget.ViewPager2
         android:id="@+id/pager"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />

+ 1 - 1
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">
 
-        <androidx.viewpager.widget.ViewPager
+        <androidx.viewpager2.widget.ViewPager2
             android:id="@+id/contentPanel"
             android:layout_width="match_parent"
             android:layout_height="0dp"

+ 1 - 1
app/src/main/res/layout/whats_new_activity.xml

@@ -24,7 +24,7 @@
         android:textAppearance="?android:attr/textAppearanceLarge"
         android:textColor="@color/primary_button_text_color"/>
 
-    <androidx.viewpager.widget.ViewPager
+    <androidx.viewpager2.widget.ViewPager2
         android:id="@+id/contentPanel"
         android:layout_width="match_parent"
         android:layout_height="0dp"

+ 8 - 11
gradle/verification-metadata.xml

@@ -5817,17 +5817,14 @@
             <sha256 value="66afb9f2eea39427f6f03c14c5b82ca240157e22b8b2a764f0a7c8ad87cb2d3e" origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
-       <component group="com.github.nextcloud" name="android-library"
-           version="cda1b08aa81b74201177f29c2326abee62f32c15">
-           <artifact name="android-library-cda1b08aa81b74201177f29c2326abee62f32c15.aar">
-               <sha256 value="39c76af292201a94cf0753f296a18deb5512d88e97537f7c4e9a766ec50c1520"
-                   origin="Generated by Gradle" reason="Artifact is not signed" />
-           </artifact>
-           <artifact name="android-library-cda1b08aa81b74201177f29c2326abee62f32c15.module">
-               <sha256 value="c7686ef2125d141196bb3e1937a12f0647e5300accc132ddc11dfe37f9db66f2"
-                   origin="Generated by Gradle" reason="Artifact is not signed" />
-           </artifact>
-       </component>
+      <component group="com.github.nextcloud" name="android-library" version="cda1b08aa81b74201177f29c2326abee62f32c15">
+         <artifact name="android-library-cda1b08aa81b74201177f29c2326abee62f32c15.aar">
+            <sha256 value="39c76af292201a94cf0753f296a18deb5512d88e97537f7c4e9a766ec50c1520" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+         <artifact name="android-library-cda1b08aa81b74201177f29c2326abee62f32c15.module">
+            <sha256 value="c7686ef2125d141196bb3e1937a12f0647e5300accc132ddc11dfe37f9db66f2" origin="Generated by Gradle" reason="Artifact is not signed"/>
+         </artifact>
+      </component>
       <component group="com.github.nextcloud" name="android-library" version="e7a13d03c1e7549a301edb8b4b58d1c5dda84123">
          <artifact name="android-library-e7a13d03c1e7549a301edb8b4b58d1c5dda84123.aar">
             <sha256 value="57ab4fd7c922875a7e0b5feac20aa27ab5df0fd3b4e042f92ed727c0b6316e81" origin="Generated by Gradle" reason="Artifact is not signed"/>