Sfoglia il codice sorgente

implement native view bindings in activities/fragments/adapters

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Andy Scherzinger 4 anni fa
parent
commit
84edbdedac

+ 40 - 52
src/main/java/com/owncloud/android/ui/activity/EditorWebView.java

@@ -28,54 +28,39 @@ import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
 import android.view.View;
 import android.webkit.JavascriptInterface;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
+import android.webkit.WebView;
 import android.widget.Toast;
 
 import com.google.android.material.snackbar.Snackbar;
 import com.nextcloud.client.account.User;
 import com.nextcloud.java.util.Optional;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.RichdocumentsWebviewBinding;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimeTypeUtil;
 import com.owncloud.android.utils.theme.ThemeSnackbarUtils;
 
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.Unbinder;
-
 public abstract class EditorWebView extends ExternalSiteWebView {
     protected Snackbar loadingSnackbar;
 
     protected String fileName;
 
-    @BindView(R.id.progressBar2)
-    ProgressBar progressBar;
-
-    @BindView(R.id.thumbnail)
-    ImageView thumbnailView;
-
-    @BindView(R.id.filename)
-    TextView fileNameTextView;
-
-    private Unbinder unbinder;
+    RichdocumentsWebviewBinding binding;
 
     protected void loadUrl(String url) {
         onUrlLoaded(url);
     }
 
     protected void hideLoading() {
-        thumbnailView.setVisibility(View.GONE);
-        fileNameTextView.setVisibility(View.GONE);
-        progressBar.setVisibility(View.GONE);
-        webview.setVisibility(View.VISIBLE);
+        binding.thumbnail.setVisibility(View.GONE);
+        binding.filename.setVisibility(View.GONE);
+        binding.progressBar2.setVisibility(View.GONE);
+        getWebView().setVisibility(View.VISIBLE);
 
         if (loadingSnackbar != null) {
             loadingSnackbar.dismiss();
@@ -86,10 +71,10 @@ public abstract class EditorWebView extends ExternalSiteWebView {
         this.url = loadedUrl;
 
         if (!url.isEmpty()) {
-            getWebview().loadUrl(url);
+            this.getWebView().loadUrl(url);
 
             new Handler().postDelayed(() -> {
-                if (getWebview().getVisibility() != View.VISIBLE) {
+                if (this.getWebView().getVisibility() != View.VISIBLE) {
                     Snackbar snackbar = DisplayUtils.createSnackbar(findViewById(android.R.id.content),
                                                                     R.string.timeout_richDocuments, Snackbar.LENGTH_INDEFINITE)
                         .setAction(R.string.common_back, v -> closeView());
@@ -107,19 +92,18 @@ public abstract class EditorWebView extends ExternalSiteWebView {
     }
 
     public void closeView() {
-        webview.destroy();
+        getWebView().destroy();
         finish();
     }
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        webViewLayout = R.layout.richdocuments_webview; // TODO rename
-
-        showToolbar = false;
-
-        super.onCreate(savedInstanceState);
+    protected void bindView() {
+        binding = RichdocumentsWebviewBinding.inflate(getLayoutInflater());
+    }
 
-        unbinder = ButterKnife.bind(this);
+    @Override
+    protected void postOnCreate() {
+        super.postOnCreate();
 
         setFile(getIntent().getParcelableExtra(ExternalSiteWebView.EXTRA_FILE));
 
@@ -141,9 +125,21 @@ public abstract class EditorWebView extends ExternalSiteWebView {
         initLoadingScreen(user.get());
     }
 
+    protected WebView getWebView() {
+        return binding.webView;
+    }
+
+    protected View getRootView() {
+        return binding.getRoot();
+    }
+
+    protected boolean showToolbarByDefault() {
+        return false;
+    }
+
     protected void initLoadingScreen(final User user) {
         setThumbnailView(user);
-        fileNameTextView.setText(fileName);
+        binding.filename.setText(fileName);
     }
 
     private void openShareDialog() {
@@ -153,24 +149,16 @@ public abstract class EditorWebView extends ExternalSiteWebView {
         startActivity(intent);
     }
 
-    @Override
-    protected void onDestroy() {
-        unbinder.unbind();
-        webview.destroy();
-
-        super.onDestroy();
-    }
-
     protected void setThumbnailView(final User user) {
         // Todo minimize: only icon by mimetype
         OCFile file = getFile();
         if (file.isFolder()) {
-            thumbnailView.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
-                                                                              file.isSharedWithSharee(),
-                                                                          file.isSharedViaLink(),
-                                                                          file.isEncrypted(),
-                                                                          file.getMountType(),
-                                                                          this));
+            binding.thumbnail.setImageDrawable(MimeTypeUtil.getFolderTypeIcon(file.isSharedWithMe() ||
+                                                                                  file.isSharedWithSharee(),
+                                                                              file.isSharedViaLink(),
+                                                                              file.isEncrypted(),
+                                                                              file.getMountType(),
+                                                                              this));
         } else {
             if ((MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file)) && file.getRemoteId() != null) {
                 // Thumbnail in cache?
@@ -180,21 +168,21 @@ public abstract class EditorWebView extends ExternalSiteWebView {
                 if (thumbnail != null && !file.isUpdateThumbnailNeeded()) {
                     if (MimeTypeUtil.isVideo(file)) {
                         Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
-                        thumbnailView.setImageBitmap(withOverlay);
+                        binding.thumbnail.setImageBitmap(withOverlay);
                     } else {
-                        thumbnailView.setImageBitmap(thumbnail);
+                        binding.thumbnail.setImageBitmap(thumbnail);
                     }
                 }
 
                 if ("image/png".equalsIgnoreCase(file.getMimeType())) {
-                    thumbnailView.setBackgroundColor(getResources().getColor(R.color.bg_default));
+                    binding.thumbnail.setBackgroundColor(getResources().getColor(R.color.bg_default));
                 }
             } else {
                 Drawable icon = MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
                                                              file.getFileName(),
                                                              user,
                                                              getApplicationContext());
-                thumbnailView.setImageDrawable(icon);
+                binding.thumbnail.setImageDrawable(icon);
             }
         }
     }
@@ -203,7 +191,7 @@ public abstract class EditorWebView extends ExternalSiteWebView {
         DownloadManager downloadmanager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
 
         if (downloadmanager == null) {
-            DisplayUtils.showSnackMessage(webview, getString(R.string.failed_to_download));
+            DisplayUtils.showSnackMessage(getWebView(), getString(R.string.failed_to_download));
             return;
         }
 

+ 47 - 33
src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java

@@ -36,6 +36,7 @@ import android.widget.ProgressBar;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.ExternalsiteWebviewBinding;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.theme.ThemeToolbarUtils;
@@ -60,19 +61,19 @@ public class ExternalSiteWebView extends FileActivity {
     private static final String TAG = ExternalSiteWebView.class.getSimpleName();
 
     protected boolean showToolbar = true;
-    protected int webViewLayout = R.layout.externalsite_webview;
+    private ExternalsiteWebviewBinding binding;
     private int menuItemId;
-    protected WebView webview;
     private boolean showSidebar;
     String url;
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    protected final void onCreate(Bundle savedInstanceState) {
         Log_OC.v(TAG, "onCreate() start");
+        bindView();
+        showToolbar = showToolbarByDefault();
 
         Bundle extras = getIntent().getExtras();
-        String title = extras.getString(EXTRA_TITLE);
-        url = extras.getString(EXTRA_URL);
+        url = getIntent().getExtras().getString(EXTRA_URL);
         if (extras.containsKey(EXTRA_SHOW_TOOLBAR)) {
             showToolbar = extras.getBoolean(EXTRA_SHOW_TOOLBAR);
         }
@@ -88,19 +89,22 @@ public class ExternalSiteWebView extends FileActivity {
 
         super.onCreate(savedInstanceState);
 
-        setContentView(webViewLayout);
+        setContentView(getRootView());
 
-        webview = findViewById(R.id.webView);
-        final WebSettings webSettings = webview.getSettings();
+        postOnCreate();
+    }
+
+    protected void postOnCreate() {
+        final WebSettings webSettings = getWebView().getSettings();
 
-        webview.setFocusable(true);
-        webview.setFocusableInTouchMode(true);
-        webview.setClickable(true);
+        getWebView().setFocusable(true);
+        getWebView().setFocusableInTouchMode(true);
+        getWebView().setClickable(true);
 
         // allow debugging (when building the debug version); see details in
         // https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews
         if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ||
-                getResources().getBoolean(R.bool.is_beta)) {
+            getResources().getBoolean(R.bool.is_beta)) {
             Log_OC.d(this, "Enable debug for webView");
             WebView.setWebContentsDebuggingEnabled(true);
         }
@@ -121,6 +125,7 @@ public class ExternalSiteWebView extends FileActivity {
             setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
         }
 
+        String title = getIntent().getExtras().getString(EXTRA_TITLE);
         if (!TextUtils.isEmpty(title)) {
             setupActionBar(title);
         }
@@ -129,25 +134,37 @@ public class ExternalSiteWebView extends FileActivity {
         final ProgressBar progressBar = findViewById(R.id.progressBar);
 
         if (progressBar != null) {
-            webview.setWebChromeClient(new WebChromeClient() {
+            getWebView().setWebChromeClient(new WebChromeClient() {
                 public void onProgressChanged(WebView view, int progress) {
                     progressBar.setProgress(progress * 1000);
                 }
             });
         }
 
-        webview.setWebViewClient(new WebViewClient() {
+        getWebView().setWebViewClient(new WebViewClient() {
             public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                 InputStream resources = getResources().openRawResource(R.raw.custom_error);
                 String customError = DisplayUtils.getData(resources);
 
                 if (!customError.isEmpty()) {
-                    webview.loadData(customError, "text/html; charset=UTF-8", null);
+                    getWebView().loadData(customError, "text/html; charset=UTF-8", null);
                 }
             }
         });
 
-        webview.loadUrl(url);
+        getWebView().loadUrl(url);
+    }
+
+    protected void bindView() {
+        binding = ExternalsiteWebviewBinding.inflate(getLayoutInflater());
+    }
+
+    protected boolean showToolbarByDefault() {
+        return true;
+    }
+
+    protected View getRootView() {
+        return binding.getRoot();
     }
 
     @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT")
@@ -199,24 +216,22 @@ public class ExternalSiteWebView extends FileActivity {
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         boolean retval;
-        switch (item.getItemId()) {
-            case android.R.id.home:
-                if (showSidebar) {
-                    if (isDrawerOpen()) {
-                        closeDrawer();
-                    } else {
-                        openDrawer();
-                    }
+
+        if (item.getItemId() == android.R.id.home) {
+            if (showSidebar) {
+                if (isDrawerOpen()) {
+                    closeDrawer();
                 } else {
-                    finish();
+                    openDrawer();
                 }
+            } else {
+                finish();
+            }
             retval = true;
-            break;
-
-            default:
-                retval = super.onOptionsItemSelected(item);
-                break;
+        } else {
+            retval = super.onOptionsItemSelected(item);
         }
+
         return retval;
     }
 
@@ -224,10 +239,9 @@ public class ExternalSiteWebView extends FileActivity {
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
         setDrawerMenuItemChecked(menuItemId);
-
     }
 
-    public WebView getWebview() {
-        return this.webview;
+    protected WebView getWebView() {
+        return binding.webView;
     }
 }

+ 12 - 18
src/main/java/com/owncloud/android/ui/activity/RichDocumentsEditorWebView.java

@@ -61,8 +61,6 @@ import java.lang.ref.WeakReference;
 import javax.inject.Inject;
 
 import androidx.annotation.NonNull;
-import butterknife.ButterKnife;
-import butterknife.Unbinder;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
@@ -78,8 +76,6 @@ public class RichDocumentsEditorWebView extends EditorWebView {
     private static final String SLIDESHOW = "slideshow";
     private static final String NEW_NAME = "NewName";
 
-    private Unbinder unbinder;
-
     public ValueCallback<Uri[]> uploadMessage;
 
     @Inject
@@ -90,14 +86,12 @@ public class RichDocumentsEditorWebView extends EditorWebView {
 
     @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT_INTERFACE")
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        unbinder = ButterKnife.bind(this);
+    protected void postOnCreate() {
+        super.postOnCreate();
 
-        webview.addJavascriptInterface(new RichDocumentsMobileInterface(), "RichDocumentsMobileInterface");
+        getWebView().addJavascriptInterface(new RichDocumentsMobileInterface(), "RichDocumentsMobileInterface");
 
-        webview.setWebChromeClient(new WebChromeClient() {
+        getWebView().setWebChromeClient(new WebChromeClient() {
             RichDocumentsEditorWebView activity = RichDocumentsEditorWebView.this;
 
             @Override
@@ -187,7 +181,7 @@ public class RichDocumentsEditorWebView extends EditorWebView {
             if (result.isSuccess()) {
                 String asset = (String) result.getSingleData();
 
-                runOnUiThread(() -> webview.evaluateJavascript("OCA.RichDocuments.documentsMain.postAsset('" +
+                runOnUiThread(() -> getWebView().evaluateJavascript("OCA.RichDocuments.documentsMain.postAsset('" +
                                                                    file.getFileName() + "', '" + asset + "');", null));
             } else {
                 runOnUiThread(() -> DisplayUtils.showSnackMessage(this, "Inserting image failed!"));
@@ -209,8 +203,7 @@ public class RichDocumentsEditorWebView extends EditorWebView {
 
     @Override
     protected void onDestroy() {
-        unbinder.unbind();
-        webview.destroy();
+        getWebView().destroy();
 
         super.onDestroy();
     }
@@ -219,15 +212,16 @@ public class RichDocumentsEditorWebView extends EditorWebView {
     protected void onResume() {
         super.onResume();
 
-        webview.evaluateJavascript("if (typeof OCA.RichDocuments.documentsMain.postGrabFocus !== 'undefined') " +
-                                       "{ OCA.RichDocuments.documentsMain.postGrabFocus(); }", null);
+        getWebView().evaluateJavascript("if (typeof OCA.RichDocuments.documentsMain.postGrabFocus !== 'undefined') " +
+                                            "{ OCA.RichDocuments.documentsMain.postGrabFocus(); }",
+                                        null);
     }
 
     private void printFile(Uri url) {
         OwnCloudAccount account = accountManager.getCurrentOwnCloudAccount();
 
         if (account == null) {
-            DisplayUtils.showSnackMessage(webview, getString(R.string.failed_to_print));
+            DisplayUtils.showSnackMessage(getWebView(), getString(R.string.failed_to_print));
             return;
         }
 
@@ -306,8 +300,8 @@ public class RichDocumentsEditorWebView extends EditorWebView {
         public void paste() {
             // Javascript cannot do this by itself, so help out.
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                webview.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PASTE));
-                webview.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PASTE));
+                getWebView().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PASTE));
+                getWebView().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_PASTE));
             }
         }
 

+ 14 - 41
src/main/java/com/owncloud/android/ui/activity/SsoGrantPermissionActivity.java

@@ -38,13 +38,11 @@ import android.text.SpannableStringBuilder;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
 import android.util.Log;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
 
 import com.nextcloud.android.sso.Constants;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.ActivitySsoGrantPermissionBinding;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.accounts.AccountUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -52,11 +50,6 @@ import com.owncloud.android.utils.EncryptionUtils;
 
 import java.util.UUID;
 
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.OnClick;
-import butterknife.Unbinder;
-
 import static com.nextcloud.android.sso.Constants.DELIMITER;
 import static com.nextcloud.android.sso.Constants.EXCEPTION_ACCOUNT_ACCESS_DECLINED;
 import static com.nextcloud.android.sso.Constants.EXCEPTION_ACCOUNT_NOT_FOUND;
@@ -76,26 +69,12 @@ public class SsoGrantPermissionActivity extends BaseActivity {
     private String packageName;
     private Account account;
 
-    private Unbinder unbinder;
-
-    @BindView(R.id.appIcon)
-    ImageView appIcon;
-
-    @BindView(R.id.permissionText)
-    TextView permissionText;
-
-    @BindView(R.id.btnGrant)
-    Button grantPermissionButton;
-
-    @BindView(R.id.btnDecline)
-    Button declinePermissionButton;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_sso_grant_permission);
 
-        unbinder = ButterKnife.bind(this);
+        ActivitySsoGrantPermissionBinding binding = ActivitySsoGrantPermissionBinding.inflate(getLayoutInflater());
+        setContentView(binding.getRoot());
 
         ComponentName callingActivity = getCallingActivity();
 
@@ -103,11 +82,10 @@ public class SsoGrantPermissionActivity extends BaseActivity {
             packageName = callingActivity.getPackageName();
             String appName = getAppNameForPackage(packageName);
             account = getIntent().getParcelableExtra(NEXTCLOUD_FILES_ACCOUNT);
-            permissionText.setText(makeSpecialPartsBold(
-                    getString(R.string.single_sign_on_request_token, appName, account.name),
-                    appName,
-                    account.name)
-            );
+            binding.permissionText.setText(makeSpecialPartsBold(
+                getString(R.string.single_sign_on_request_token, appName, account.name),
+                appName,
+                account.name));
             Log.v(TAG, "TOKEN-REQUEST: Calling Package: " + packageName);
             Log.v(TAG, "TOKEN-REQUEST: App Name: " + appName);
         } else {
@@ -119,17 +97,14 @@ public class SsoGrantPermissionActivity extends BaseActivity {
         try {
             if (packageName != null) {
                 Drawable appIcon = getPackageManager().getApplicationIcon(packageName);
-                this.appIcon.setImageDrawable(appIcon);
+                binding.appIcon.setImageDrawable(appIcon);
             }
         } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, e.getMessage());
+            Log.e(TAG, "Error retrieving app icon", e);
         }
-    }
 
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        unbinder.unbind();
+        binding.btnDecline.setOnClickListener(v -> exitFailed());
+        binding.btnGrant.setOnClickListener(v -> grantPermission());
     }
 
     private SpannableStringBuilder makeSpecialPartsBold(String text, String... toBeStyledText) {
@@ -158,18 +133,16 @@ public class SsoGrantPermissionActivity extends BaseActivity {
         try {
             ai = pm.getApplicationInfo(pkg, 0);
         } catch (final PackageManager.NameNotFoundException e) {
-            Log.e(TAG, e.getMessage());
+            Log.e(TAG, "Error fetching app name for package", e);
         }
         return (String) (ai != null ? pm.getApplicationLabel(ai) : "(unknown)");
     }
 
-    @OnClick(R.id.btnDecline)
-    void exitFailed() {
+    private void exitFailed() {
         setResultAndExit(EXCEPTION_ACCOUNT_ACCESS_DECLINED);
     }
 
-    @OnClick(R.id.btnGrant)
-    void grantPermission() {
+    private void grantPermission() {
         // create token
         SharedPreferences sharedPreferences = getSharedPreferences(SSO_SHARED_PREFERENCE, Context.MODE_PRIVATE);
         String token = UUID.randomUUID().toString().replaceAll("-", "");

+ 4 - 5
src/main/java/com/owncloud/android/ui/activity/TextEditorWebView.kt

@@ -23,7 +23,6 @@ package com.owncloud.android.ui.activity
 
 import android.annotation.SuppressLint
 import android.net.Uri
-import android.os.Bundle
 import android.widget.Toast
 import androidx.webkit.WebSettingsCompat
 import androidx.webkit.WebViewFeature
@@ -42,8 +41,8 @@ class TextEditorWebView : EditorWebView() {
     lateinit var deviceInfo: DeviceInfo
 
     @SuppressLint("AddJavascriptInterface") // suppress warning as webview is only used > Lollipop
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
+    override fun postOnCreate() {
+        super.postOnCreate()
 
         if (!user.isPresent) {
             Toast.makeText(this, getString(R.string.failed_to_start_editor), Toast.LENGTH_LONG).show()
@@ -53,10 +52,10 @@ class TextEditorWebView : EditorWebView() {
         val editor = FileMenuFilter.getEditor(contentResolver, user.get(), file.mimeType)
 
         if (editor != null && editor.id == "onlyoffice") {
-            webview.settings.userAgentString = generateOnlyOfficeUserAgent()
+            webView.settings.userAgentString = generateOnlyOfficeUserAgent()
         }
 
-        webview.addJavascriptInterface(MobileInterface(), "DirectEditingMobileInterface")
+        webView.addJavascriptInterface(MobileInterface(), "DirectEditingMobileInterface")
 
         if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) {
             WebSettingsCompat.setForceDarkStrategy(

+ 15 - 16
src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java

@@ -40,7 +40,6 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.webkit.URLUtil;
 import android.widget.ImageView;
-import android.widget.TextView;
 
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.request.animation.GlideAnimation;
@@ -50,6 +49,7 @@ import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.nextcloud.common.NextcloudClient;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.UserInfoDetailsTableItemBinding;
 import com.owncloud.android.databinding.UserInfoLayoutBinding;
 import com.owncloud.android.lib.common.OwnCloudClientFactory;
 import com.owncloud.android.lib.common.UserInfo;
@@ -83,8 +83,6 @@ import androidx.core.graphics.drawable.DrawableCompat;
 import androidx.fragment.app.FragmentManager;
 import androidx.lifecycle.Lifecycle;
 import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 /**
  * This Activity presents the user information.
@@ -381,13 +379,11 @@ public class UserInfoActivity extends DrawerActivity implements Injectable {
         @ColorInt protected int mTintColor;
 
         public static class ViewHolder extends RecyclerView.ViewHolder {
+            protected UserInfoDetailsTableItemBinding binding;
 
-            @BindView(R.id.icon) protected ImageView icon;
-            @BindView(R.id.text) protected TextView text;
-
-            public ViewHolder(View itemView) {
-                super(itemView);
-                ButterKnife.bind(this, itemView);
+            public ViewHolder(UserInfoDetailsTableItemBinding binding) {
+                super(binding.getRoot());
+                this.binding = binding;
             }
         }
 
@@ -404,18 +400,21 @@ public class UserInfoActivity extends DrawerActivity implements Injectable {
         @NonNull
         @Override
         public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            View view = inflater.inflate(R.layout.user_info_details_table_item, parent, false);
-            return new ViewHolder(view);
+            return new ViewHolder(
+                UserInfoDetailsTableItemBinding.inflate(
+                    LayoutInflater.from(parent.getContext()),
+                    parent,
+                    false)
+            );
         }
 
         @Override
         public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
             UserInfoDetailsItem item = mDisplayList.get(position);
-            holder.icon.setImageResource(item.icon);
-            holder.text.setText(item.text);
-            holder.icon.setContentDescription(item.iconContentDescription);
-            DrawableCompat.setTint(holder.icon.getDrawable(), mTintColor);
+            holder.binding.icon.setImageResource(item.icon);
+            holder.binding.text.setText(item.text);
+            holder.binding.icon.setContentDescription(item.iconContentDescription);
+            DrawableCompat.setTint(holder.binding.icon.getDrawable(), mTintColor);
         }
 
         @Override

+ 284 - 130
src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

@@ -43,7 +43,6 @@ import android.view.WindowManager;
 import android.widget.Filter;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import com.elyeproj.loaderviewlibrary.LoaderImageView;
@@ -51,6 +50,11 @@ import com.nextcloud.client.account.User;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.GridImageBinding;
+import com.owncloud.android.databinding.GridItemBinding;
+import com.owncloud.android.databinding.ListFooterBinding;
+import com.owncloud.android.databinding.ListHeaderBinding;
+import com.owncloud.android.databinding.ListItemBinding;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
@@ -96,8 +100,6 @@ import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.content.res.ResourcesCompat;
 import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 /**
  * This Adapter populates a RecyclerView with all files and folders in a Nextcloud instance.
@@ -293,34 +295,42 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
             default:
             case VIEWTYPE_ITEM:
                 if (gridView) {
-                    View itemView = LayoutInflater.from(activity).inflate(R.layout.grid_item, parent, false);
-                    return new OCFileListGridItemViewHolder(itemView);
+                    return new OCFileListGridItemViewHolder(
+                        GridItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+                    );
                 } else {
-                    View itemView = LayoutInflater.from(activity).inflate(R.layout.list_item, parent, false);
-                    return new OCFileListItemViewHolder(itemView);
+                    return new OCFileListItemViewHolder(
+                        ListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+                    );
                 }
 
             case VIEWTYPE_IMAGE:
                 if (gridView) {
-                    View itemView = LayoutInflater.from(activity).inflate(R.layout.grid_image, parent, false);
-                    return new OCFileListGridImageViewHolder(itemView);
+                    return new OCFileListGridImageViewHolder(
+                        GridImageBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+                    );
                 } else {
-                    View itemView = LayoutInflater.from(activity).inflate(R.layout.list_item, parent, false);
-                    return new OCFileListItemViewHolder(itemView);
+                    return new OCFileListItemViewHolder(
+                        ListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+                    );
                 }
 
             case VIEWTYPE_FOOTER:
-                View itemView = LayoutInflater.from(activity).inflate(R.layout.list_footer, parent, false);
-                return new OCFileListFooterViewHolder(itemView);
+                return new OCFileListFooterViewHolder(
+                    ListFooterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+                );
 
             case VIEWTYPE_HEADER:
-                View headerView = LayoutInflater.from(activity).inflate(R.layout.list_header, parent, false);
+                ListHeaderBinding binding = ListHeaderBinding.inflate(
+                    LayoutInflater.from(parent.getContext()),
+                    parent,
+                    false);
 
-                ViewGroup.LayoutParams layoutParams = headerView.getLayoutParams();
+                ViewGroup.LayoutParams layoutParams = binding.headerView.getLayoutParams();
                 layoutParams.height = (int) (parent.getHeight() * 0.3);
-                headerView.setLayoutParams(layoutParams);
+                binding.headerView.setLayoutParams(layoutParams);
 
-                return new OCFileListHeaderViewHolder(headerView);
+                return new OCFileListHeaderViewHolder(binding);
         }
     }
 
@@ -328,72 +338,72 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
     public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
         if (holder instanceof OCFileListFooterViewHolder) {
             OCFileListFooterViewHolder footerViewHolder = (OCFileListFooterViewHolder) holder;
-            footerViewHolder.footerText.setText(getFooterText());
-            footerViewHolder.progressBar.getIndeterminateDrawable()
+            footerViewHolder.binding.footerText.setText(getFooterText());
+            footerViewHolder.binding.loadingProgressBar.getIndeterminateDrawable()
                 .setColorFilter(ThemeColorUtils.primaryColor(activity), PorterDuff.Mode.SRC_IN);
-            footerViewHolder.progressBar.setVisibility(
+            footerViewHolder.binding.loadingProgressBar.setVisibility(
                 ocFileListFragmentInterface.isLoading() ? View.VISIBLE : View.GONE);
         } else if (holder instanceof OCFileListHeaderViewHolder) {
             OCFileListHeaderViewHolder headerViewHolder = (OCFileListHeaderViewHolder) holder;
             String text = currentDirectory.getRichWorkspace();
 
-            PreviewTextFragment.setText(headerViewHolder.headerText, text, null, activity, true, true);
-            headerViewHolder.headerView.setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked());
+            PreviewTextFragment.setText(headerViewHolder.binding.headerText, text, null, activity, true, true);
+            headerViewHolder.binding.headerView.setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked());
         } else {
-            OCFileListGridImageViewHolder gridViewHolder = (OCFileListGridImageViewHolder) holder;
+            ListGridImageViewHolder gridViewHolder = (ListGridImageViewHolder) holder;
 
             OCFile file = getItem(position);
 
             boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file);
 
-            gridViewHolder.thumbnail.setTag(file.getFileId());
+            gridViewHolder.getThumbnail().setTag(file.getFileId());
             setThumbnail(file,
-                         gridViewHolder.thumbnail,
+                         gridViewHolder.getThumbnail(),
                          user,
                          mStorageManager,
                          asyncTasks,
                          gridView,
                          activity,
-                         gridViewHolder.shimmerThumbnail, preferences);
+                         gridViewHolder.getShimmerThumbnail(), preferences);
 
             if (highlightedItem != null && file.getFileId() == highlightedItem.getFileId()) {
-                gridViewHolder.itemLayout.setBackgroundColor(activity.getResources()
+                gridViewHolder.getItemLayout().setBackgroundColor(activity.getResources()
                                                                  .getColor(R.color.selected_item_background));
             } else if (isCheckedFile(file)) {
-                gridViewHolder.itemLayout.setBackgroundColor(activity.getResources()
+                gridViewHolder.getItemLayout().setBackgroundColor(activity.getResources()
                                                                  .getColor(R.color.selected_item_background));
-                gridViewHolder.checkbox.setImageDrawable(
+                gridViewHolder.getCheckbox().setImageDrawable(
                     ThemeDrawableUtils.tintDrawable(R.drawable.ic_checkbox_marked,
                                                     ThemeColorUtils.primaryColor(activity)));
             } else {
-                gridViewHolder.itemLayout.setBackgroundColor(activity.getResources().getColor(R.color.bg_default));
-                gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline);
+                gridViewHolder.getItemLayout().setBackgroundColor(activity.getResources().getColor(R.color.bg_default));
+                gridViewHolder.getCheckbox().setImageResource(R.drawable.ic_checkbox_blank_outline);
             }
 
-            gridViewHolder.itemLayout.setOnClickListener(v -> ocFileListFragmentInterface.onItemClicked(file));
+            gridViewHolder.getItemLayout().setOnClickListener(v -> ocFileListFragmentInterface.onItemClicked(file));
 
             if (!hideItemOptions) {
-                gridViewHolder.itemLayout.setLongClickable(true);
-                gridViewHolder.itemLayout.setOnLongClickListener(v ->
+                gridViewHolder.getItemLayout().setLongClickable(true);
+                gridViewHolder.getItemLayout().setOnLongClickListener(v ->
                                                                      ocFileListFragmentInterface.onLongItemClicked(file));
             }
 
             // unread comments
             if (file.getUnreadCommentsCount() > 0) {
-                gridViewHolder.unreadComments.setVisibility(View.VISIBLE);
-                gridViewHolder.unreadComments.setOnClickListener(view -> ocFileListFragmentInterface
+                gridViewHolder.getUnreadComments().setVisibility(View.VISIBLE);
+                gridViewHolder.getUnreadComments().setOnClickListener(view -> ocFileListFragmentInterface
                     .showActivityDetailView(file));
             } else {
-                gridViewHolder.unreadComments.setVisibility(View.GONE);
+                gridViewHolder.getUnreadComments().setVisibility(View.GONE);
             }
 
-            if (holder instanceof OCFileListItemViewHolder) {
-                OCFileListItemViewHolder itemViewHolder = (OCFileListItemViewHolder) holder;
+            if (holder instanceof ListItemViewHolder) {
+                ListItemViewHolder itemViewHolder = (ListItemViewHolder) holder;
 
                 if ((file.isSharedWithMe() || file.isSharedWithSharee()) && !multiSelect && !gridView &&
                     !hideItemOptions) {
-                    itemViewHolder.sharedAvatars.setVisibility(View.VISIBLE);
-                    itemViewHolder.sharedAvatars.removeAllViews();
+                    itemViewHolder.getSharedAvatars().setVisibility(View.VISIBLE);
+                    itemViewHolder.getSharedAvatars().removeAllViews();
 
                     String fileOwner = file.getOwnerId();
                     List<ShareeUser> sharees = file.getSharees();
@@ -410,12 +420,12 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
 
                     Log_OC.d(this, "sharees of " + file.getFileName() + ": " + sharees);
 
-                    itemViewHolder.sharedAvatars.setAvatars(user, sharees);
-                    itemViewHolder.sharedAvatars.setOnClickListener(
+                    itemViewHolder.getSharedAvatars().setAvatars(user, sharees);
+                    itemViewHolder.getSharedAvatars().setOnClickListener(
                         view -> ocFileListFragmentInterface.onShareIconClick(file));
                 } else {
-                    itemViewHolder.sharedAvatars.setVisibility(View.GONE);
-                    itemViewHolder.sharedAvatars.removeAllViews();
+                    itemViewHolder.getSharedAvatars().setVisibility(View.GONE);
+                    itemViewHolder.getSharedAvatars().removeAllViews();
                 }
 
                 // npe fix: looks like file without local storage path somehow get here
@@ -429,78 +439,78 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
                         localSize = localFile.length();
                     }
 
-                    itemViewHolder.fileSize.setText(DisplayUtils.bytesToHumanReadable(localSize));
+                    itemViewHolder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(localSize));
                 } else {
-                    itemViewHolder.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
+                    itemViewHolder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
                 }
-                itemViewHolder.lastModification.setText(DisplayUtils.getRelativeTimestamp(activity,
+                itemViewHolder.getLastModification().setText(DisplayUtils.getRelativeTimestamp(activity,
                                                                                           file.getModificationTimestamp()));
 
                 if (multiSelect || gridView || hideItemOptions) {
-                    itemViewHolder.overflowMenu.setVisibility(View.GONE);
+                    itemViewHolder.getOverflowMenu().setVisibility(View.GONE);
                 } else {
-                    itemViewHolder.overflowMenu.setVisibility(View.VISIBLE);
-                    itemViewHolder.overflowMenu.setOnClickListener(view -> ocFileListFragmentInterface
+                    itemViewHolder.getOverflowMenu().setVisibility(View.VISIBLE);
+                    itemViewHolder.getOverflowMenu().setOnClickListener(view -> ocFileListFragmentInterface
                         .onOverflowIconClicked(file, view));
                 }
             }
 
-            gridViewHolder.localFileIndicator.setVisibility(View.INVISIBLE);   // default first
+            gridViewHolder.getLocalFileIndicator().setVisibility(View.INVISIBLE);   // default first
 
             OperationsService.OperationsServiceBinder operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder();
             FileDownloader.FileDownloaderBinder fileDownloaderBinder = transferServiceGetter.getFileDownloaderBinder();
             FileUploader.FileUploaderBinder fileUploaderBinder = transferServiceGetter.getFileUploaderBinder();
             if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(user, file)) {
                 //synchronizing
-                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing);
-                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
+                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
 
             } else if (fileDownloaderBinder != null && fileDownloaderBinder.isDownloading(user, file)) {
                 // downloading
-                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing);
-                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
+                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
 
             } else if (fileUploaderBinder != null && fileUploaderBinder.isUploading(user, file)) {
                 //uploading
-                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing);
-                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
+                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
 
             } else if (file.getEtagInConflict() != null) {
                 // conflict
-                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing_error);
-                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing_error);
+                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
 
             } else if (file.isDown()) {
-                gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synced);
-                gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE);
+                gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synced);
+                gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
             }
 
-            gridViewHolder.favorite.setVisibility(file.isFavorite() ? View.VISIBLE : View.GONE);
+            gridViewHolder.getFavorite().setVisibility(file.isFavorite() ? View.VISIBLE : View.GONE);
 
             if (multiSelect) {
-                gridViewHolder.checkbox.setVisibility(View.VISIBLE);
+                gridViewHolder.getCheckbox().setVisibility(View.VISIBLE);
             } else {
-                gridViewHolder.checkbox.setVisibility(View.GONE);
+                gridViewHolder.getCheckbox().setVisibility(View.GONE);
             }
 
-            if (holder instanceof OCFileListGridItemViewHolder) {
-                OCFileListGridItemViewHolder gridItemViewHolder = (OCFileListGridItemViewHolder) holder;
+            if (holder instanceof ListGridItemViewHolder) {
+                ListGridItemViewHolder gridItemViewHolder = (ListGridItemViewHolder) holder;
 
-                gridItemViewHolder.fileName.setText(file.getDecryptedFileName());
+                gridItemViewHolder.getFileName().setText(file.getDecryptedFileName());
 
                 if (gridView && gridImage) {
-                    gridItemViewHolder.fileName.setVisibility(View.GONE);
+                    gridItemViewHolder.getFileName().setVisibility(View.GONE);
                 } else {
                     if (gridView && ocFileListFragmentInterface.getColumnsCount() > showFilenameColumnThreshold) {
-                        gridItemViewHolder.fileName.setVisibility(View.GONE);
+                        gridItemViewHolder.getFileName().setVisibility(View.GONE);
                     } else {
-                        gridItemViewHolder.fileName.setVisibility(View.VISIBLE);
+                        gridItemViewHolder.getFileName().setVisibility(View.VISIBLE);
                     }
                 }
             }
 
             if (gridView || hideItemOptions || (file.isFolder() && !file.canReshare())) {
-                gridViewHolder.shared.setVisibility(View.GONE);
+                gridViewHolder.getShared().setVisibility(View.GONE);
             } else {
                 showShareIcon(gridViewHolder, file);
             }
@@ -622,8 +632,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
 
     @Override
     public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
-        if (holder instanceof OCFileListGridImageViewHolder) {
-            LoaderImageView thumbnailShimmer = ((OCFileListGridImageViewHolder) holder).shimmerThumbnail;
+        if (holder instanceof ListGridImageViewHolder) {
+            LoaderImageView thumbnailShimmer = ((ListGridImageViewHolder) holder).getShimmerThumbnail();
             if (thumbnailShimmer.getVisibility() == View.VISIBLE){
                 thumbnailShimmer.setImageResource(R.drawable.background);
                 thumbnailShimmer.resetLoader();
@@ -764,8 +774,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         }
     }
 
-    private void showShareIcon(OCFileListGridImageViewHolder gridViewHolder, OCFile file) {
-        ImageView sharedIconView = gridViewHolder.shared;
+    private void showShareIcon(ListGridImageViewHolder gridViewHolder, OCFile file) {
+        ImageView sharedIconView = gridViewHolder.getShared();
 
         if (gridViewHolder instanceof OCFileListItemViewHolder || file.getUnreadCommentsCount() == 0) {
             sharedIconView.setVisibility(View.VISIBLE);
@@ -1177,89 +1187,233 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
         currentDirectory = folder;
     }
 
-    static class OCFileListItemViewHolder extends OCFileListGridItemViewHolder {
-        @BindView(R.id.file_size)
-        public TextView fileSize;
+    static class OCFileListItemViewHolder extends RecyclerView.ViewHolder implements ListItemViewHolder{
+        protected ListItemBinding binding;
+
+        private OCFileListItemViewHolder(ListItemBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+            this.binding.favoriteAction.getDrawable().mutate();
+        }
+
+        @Override
+        public TextView getFileSize() {
+            return binding.fileSize;
+        }
+
+        @Override
+        public TextView getLastModification() {
+            return binding.lastMod;
+        }
+
+        @Override
+        public ImageView getOverflowMenu() {
+            return binding.overflowMenu;
+        }
+
+        @Override
+        public AvatarGroupLayout getSharedAvatars() {
+            return binding.sharedAvatars;
+        }
+
+        @Override
+        public TextView getFileName() {
+            return binding.Filename;
+        }
+
+        @Override
+        public ImageView getThumbnail() {
+            return binding.thumbnail;
+        }
+
+        @Override
+        public LoaderImageView getShimmerThumbnail() {
+            return binding.thumbnailShimmer;
+        }
+
+        @Override
+        public ImageView getFavorite() {
+            return binding.favoriteAction;
+        }
 
-        @BindView(R.id.last_mod)
-        public TextView lastModification;
+        @Override
+        public ImageView getLocalFileIndicator() {
+            return binding.localFileIndicator;
+        }
 
-        @BindView(R.id.overflow_menu)
-        public ImageView overflowMenu;
+        @Override
+        public ImageView getShared() {
+            return binding.sharedIcon;
+        }
 
-        @BindView(R.id.sharedAvatars)
-        public AvatarGroupLayout sharedAvatars;
+        @Override
+        public ImageView getCheckbox() {
+            return binding.customCheckbox;
+        }
+
+        @Override
+        public View getItemLayout() {
+            return binding.ListItemLayout;
+        }
 
-        private OCFileListItemViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        @Override
+        public ImageView getUnreadComments() {
+            return binding.unreadComments;
         }
     }
 
-    static class OCFileListGridItemViewHolder extends OCFileListGridImageViewHolder {
-        @BindView(R.id.Filename) public TextView fileName;
+    static class OCFileListGridItemViewHolder extends RecyclerView.ViewHolder implements ListGridItemViewHolder {
+        protected GridItemBinding binding;
 
-        private OCFileListGridItemViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private OCFileListGridItemViewHolder(GridItemBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+            this.binding.favoriteAction.getDrawable().mutate();
+        }
+
+        @Override
+        public TextView getFileName() {
+            return binding.Filename;
+        }
+
+        @Override
+        public ImageView getThumbnail() {
+            return binding.thumbnail;
+        }
+
+        @Override
+        public LoaderImageView getShimmerThumbnail() {
+            return binding.thumbnailShimmer;
+        }
+
+        @Override
+        public ImageView getFavorite() {
+            return binding.favoriteAction;
+        }
+
+        @Override
+        public ImageView getLocalFileIndicator() {
+            return binding.localFileIndicator;
+        }
+
+        @Override
+        public ImageView getShared() {
+            return binding.sharedIcon;
+        }
+
+        @Override
+        public ImageView getCheckbox() {
+            return binding.customCheckbox;
+        }
+
+        @Override
+        public View getItemLayout() {
+            return binding.ListItemLayout;
+        }
+
+        @Override
+        public ImageView getUnreadComments() {
+            return binding.unreadComments;
         }
     }
 
-    static class OCFileListGridImageViewHolder extends RecyclerView.ViewHolder {
-        @BindView(R.id.thumbnail)
-        public ImageView thumbnail;
+    static class OCFileListGridImageViewHolder extends RecyclerView.ViewHolder implements ListGridImageViewHolder {
+        protected GridImageBinding binding;
 
-        @BindView(R.id.thumbnail_shimmer)
-        public LoaderImageView shimmerThumbnail;
+        private OCFileListGridImageViewHolder(GridImageBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+            this.binding.favoriteAction.getDrawable().mutate();
+        }
 
-        @BindView(R.id.favorite_action)
-        public ImageView favorite;
+        @Override
+        public ImageView getThumbnail() {
+            return binding.thumbnail;
+        }
 
-        @BindView(R.id.localFileIndicator)
-        public ImageView localFileIndicator;
+        @Override
+        public LoaderImageView getShimmerThumbnail() {
+            return binding.thumbnailShimmer;
+        }
 
-        @BindView(R.id.sharedIcon)
-        public ImageView shared;
+        @Override
+        public ImageView getFavorite() {
+            return binding.favoriteAction;
+        }
 
-        @BindView(R.id.custom_checkbox)
-        public ImageView checkbox;
+        @Override
+        public ImageView getLocalFileIndicator() {
+            return binding.localFileIndicator;
+        }
 
-        @BindView(R.id.ListItemLayout)
-        public View itemLayout;
+        @Override
+        public ImageView getShared() {
+            return binding.sharedIcon;
+        }
 
-        @BindView(R.id.unreadComments)
-        public ImageView unreadComments;
+        @Override
+        public ImageView getCheckbox() {
+            return binding.customCheckbox;
+        }
+
+        @Override
+        public View getItemLayout() {
+            return binding.ListItemLayout;
+        }
 
-        private OCFileListGridImageViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
-            favorite.getDrawable().mutate();
+        @Override
+        public ImageView getUnreadComments() {
+            return binding.unreadComments;
         }
     }
 
     static class OCFileListFooterViewHolder extends RecyclerView.ViewHolder {
-        @BindView(R.id.footerText)
-        public TextView footerText;
+        protected ListFooterBinding binding;
 
-        @BindView(R.id.loadingProgressBar)
-        public ProgressBar progressBar;
-
-        private OCFileListFooterViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private OCFileListFooterViewHolder(ListFooterBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 
     static class OCFileListHeaderViewHolder extends RecyclerView.ViewHolder {
-        @BindView(R.id.headerView)
-        public View headerView;
-
-        @BindView(R.id.headerText)
-        public TextView headerText;
+        protected ListHeaderBinding binding;
 
-        private OCFileListHeaderViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private OCFileListHeaderViewHolder(ListHeaderBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
+
+    interface ListGridImageViewHolder {
+        ImageView getThumbnail();
+
+        LoaderImageView getShimmerThumbnail();
+
+        ImageView getFavorite();
+
+        ImageView getLocalFileIndicator();
+
+        ImageView getShared();
+
+        ImageView getCheckbox();
+
+        View getItemLayout();
+
+        ImageView getUnreadComments();
+    }
+
+    interface ListGridItemViewHolder extends ListGridImageViewHolder {
+        TextView getFileName();
+    }
+
+    interface ListItemViewHolder extends ListGridItemViewHolder {
+        TextView getFileSize();
+
+        TextView getLastModification();
+
+        ImageView getOverflowMenu();
+
+        AvatarGroupLayout getSharedAvatars();
+    }
 }

+ 10 - 17
src/main/java/com/owncloud/android/ui/adapter/StoragePathAdapter.java

@@ -23,17 +23,13 @@ package com.owncloud.android.ui.adapter;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
 
-import com.owncloud.android.R;
+import com.owncloud.android.databinding.StoragePathItemBinding;
 
 import java.util.List;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 public class StoragePathAdapter extends RecyclerView.Adapter<StoragePathAdapter.StoragePathViewHolder> {
     private List<StoragePathItem> pathList;
@@ -47,8 +43,9 @@ public class StoragePathAdapter extends RecyclerView.Adapter<StoragePathAdapter.
     @NonNull
     @Override
     public StoragePathViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.storage_path_item, parent, false);
-        return new StoragePathAdapter.StoragePathViewHolder(v);
+        return new StoragePathAdapter.StoragePathViewHolder(
+            StoragePathItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+        );
     }
 
     @Override
@@ -56,8 +53,8 @@ public class StoragePathAdapter extends RecyclerView.Adapter<StoragePathAdapter.
         if (pathList != null && pathList.size() > position) {
             StoragePathItem storagePathItem = pathList.get(position);
 
-            holder.icon.setImageResource(storagePathItem.getIcon());
-            holder.name.setText(storagePathItem.getName());
+            holder.binding.icon.setImageResource(storagePathItem.getIcon());
+            holder.binding.name.setText(storagePathItem.getName());
         }
     }
 
@@ -76,15 +73,11 @@ public class StoragePathAdapter extends RecyclerView.Adapter<StoragePathAdapter.
     }
 
     class StoragePathViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
-        @BindView(R.id.icon)
-        ImageView icon;
-        @BindView(R.id.name)
-        TextView name;
+        StoragePathItemBinding binding;
 
-        public StoragePathViewHolder(@NonNull View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
-            itemView.setOnClickListener(this);
+        public StoragePathViewHolder(StoragePathItemBinding binding) {
+            super(binding.getRoot());
+            binding.getRoot().setOnClickListener(this);
         }
 
         @Override

+ 67 - 82
src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java

@@ -27,16 +27,16 @@ import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
 
 import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
 import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
 import com.nextcloud.client.core.Clock;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.GridSyncItemBinding;
+import com.owncloud.android.databinding.SyncedFoldersEmptyBinding;
+import com.owncloud.android.databinding.SyncedFoldersFooterBinding;
+import com.owncloud.android.databinding.SyncedFoldersItemHeaderBinding;
 import com.owncloud.android.datamodel.MediaFolderType;
 import com.owncloud.android.datamodel.SyncedFolderDisplayItem;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
@@ -49,8 +49,6 @@ import java.util.List;
 import java.util.Locale;
 
 import androidx.annotation.NonNull;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 /**
  * Adapter to display all auto-synced folders and/or instant upload media folders.
@@ -252,38 +250,39 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
     public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int section, boolean expanded) {
         if (section < filteredSyncFolderItems.size()) {
             HeaderViewHolder holder = (HeaderViewHolder) commonHolder;
-            holder.mainHeaderContainer.setVisibility(View.VISIBLE);
+            holder.binding.headerContainer.setVisibility(View.VISIBLE);
 
-            holder.title.setText(filteredSyncFolderItems.get(section).getFolderName());
+            holder.binding.title.setText(filteredSyncFolderItems.get(section).getFolderName());
 
             if (MediaFolderType.VIDEO == filteredSyncFolderItems.get(section).getType()) {
-                holder.type.setImageResource(R.drawable.video_32dp);
+                holder.binding.type.setImageResource(R.drawable.video_32dp);
             } else if (MediaFolderType.IMAGE == filteredSyncFolderItems.get(section).getType()) {
-                holder.type.setImageResource(R.drawable.image_32dp);
+                holder.binding.type.setImageResource(R.drawable.image_32dp);
             } else {
-                holder.type.setImageResource(R.drawable.folder_star_32dp);
+                holder.binding.type.setImageResource(R.drawable.folder_star_32dp);
             }
 
-            holder.syncStatusButton.setVisibility(View.VISIBLE);
-            holder.syncStatusButton.setTag(section);
-            holder.syncStatusButton.setOnClickListener(v -> {
+            holder.binding.syncStatusButton.setVisibility(View.VISIBLE);
+            holder.binding.syncStatusButton.setTag(section);
+            holder.binding.syncStatusButton.setOnClickListener(v -> {
                 filteredSyncFolderItems.get(section).setEnabled(
                     !filteredSyncFolderItems.get(section).isEnabled(),
                     clock.getCurrentTime()
                 );
-                setSyncButtonActiveIcon(holder.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled());
+                setSyncButtonActiveIcon(
+                    holder.binding.syncStatusButton,
+                    filteredSyncFolderItems.get(section).isEnabled());
                 clickListener.onSyncStatusToggleClick(section, filteredSyncFolderItems.get(section));
             });
-            setSyncButtonActiveIcon(holder.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled());
+            setSyncButtonActiveIcon(holder.binding.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled());
 
             if (light) {
-                holder.menuButton.setVisibility(View.GONE);
+                holder.binding.settingsButton.setVisibility(View.GONE);
             } else {
-                holder.menuButton.setVisibility(View.VISIBLE);
-                holder.menuButton.setTag(section);
-                holder.menuButton.setOnClickListener(v -> onOverflowIconClicked(section,
-                                                                                filteredSyncFolderItems.get(section),
-                                                                                v));
+                holder.binding.settingsButton.setVisibility(View.VISIBLE);
+                holder.binding.settingsButton.setTag(section);
+                holder.binding.settingsButton.setOnClickListener(
+                    v -> onOverflowIconClicked(section, filteredSyncFolderItems.get(section), v));
             }
         }
     }
@@ -313,8 +312,8 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
     public void onBindFooterViewHolder(SectionedViewHolder holder, int section) {
         if (isLastSection(section) && showFooter()) {
             FooterViewHolder footerHolder = (FooterViewHolder) holder;
-            footerHolder.title.setOnClickListener(v -> toggleHiddenItemsVisibility());
-            footerHolder.title.setText(
+            footerHolder.binding.footerText.setOnClickListener(v -> toggleHiddenItemsVisibility());
+            footerHolder.binding.footerText.setText(
                 context.getResources().getQuantityString(
                     R.plurals.synced_folders_show_hidden_folders,
                     getHiddenFolderCount(),
@@ -333,30 +332,34 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
             File file = new File(filteredSyncFolderItems.get(section).getFilePaths().get(relativePosition));
 
             ThumbnailsCacheManager.MediaThumbnailGenerationTask task =
-                    new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.image, context);
+                    new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.binding.thumbnail, context);
 
             ThumbnailsCacheManager.AsyncMediaThumbnailDrawable asyncDrawable =
                     new ThumbnailsCacheManager.AsyncMediaThumbnailDrawable(
                         context.getResources(),
                         ThumbnailsCacheManager.mDefaultImg
                     );
-            holder.image.setImageDrawable(asyncDrawable);
+            holder.binding.thumbnail.setImageDrawable(asyncDrawable);
 
             task.execute(file);
 
             // set proper tag
-            holder.image.setTag(file.hashCode());
+            holder.binding.thumbnail.setTag(file.hashCode());
 
             holder.itemView.setTag(relativePosition % gridWidth);
 
-            if (filteredSyncFolderItems.get(section).getNumberOfFiles() > gridTotal && relativePosition >= gridTotal - 1) {
-                holder.counterValue.setText(String.format(Locale.US, "%d",
-                                                          filteredSyncFolderItems.get(section).getNumberOfFiles() - gridTotal));
-                holder.counterBar.setVisibility(View.VISIBLE);
-                holder.thumbnailDarkener.setVisibility(View.VISIBLE);
+            if (filteredSyncFolderItems.get(section).getNumberOfFiles() > gridTotal &&
+                relativePosition >= gridTotal - 1) {
+                holder.binding.counter.setText(
+                    String.format(
+                        Locale.US,
+                        "%d",
+                        filteredSyncFolderItems.get(section).getNumberOfFiles() - gridTotal));
+                holder.binding.counterLayout.setVisibility(View.VISIBLE);
+                holder.binding.thumbnailDarkener.setVisibility(View.VISIBLE);
             } else {
-                holder.counterBar.setVisibility(View.GONE);
-                holder.thumbnailDarkener.setVisibility(View.GONE);
+                holder.binding.counterLayout.setVisibility(View.GONE);
+                holder.binding.thumbnailDarkener.setVisibility(View.GONE);
             }
         }
     }
@@ -365,17 +368,21 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
     @Override
     public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         if (viewType == VIEW_TYPE_HEADER) {
-            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_item_header, parent, false);
-            return new HeaderViewHolder(v);
+            return new HeaderViewHolder(
+                SyncedFoldersItemHeaderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         } else if (viewType == VIEW_TYPE_FOOTER) {
-            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_footer, parent, false);
-            return new FooterViewHolder(v);
+            return new FooterViewHolder(
+                SyncedFoldersFooterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         } else if (viewType == VIEW_TYPE_EMPTY) {
-            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_empty, parent, false);
-            return new EmptyViewHolder(v);
+            return new EmptyViewHolder(
+                SyncedFoldersEmptyBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         } else {
-            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_sync_item, parent, false);
-            return new MainViewHolder(v);
+            return new MainViewHolder(
+                GridSyncItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         }
     }
 
@@ -398,66 +405,44 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter<SectionedV
     }
 
     static class HeaderViewHolder extends SectionedViewHolder {
-        @BindView(R.id.header_container)
-        public RelativeLayout mainHeaderContainer;
-
-        @BindView(R.id.type)
-        public ImageView type;
-
-        @BindView(R.id.title)
-        public TextView title;
+        protected SyncedFoldersItemHeaderBinding binding;
 
-        @BindView(R.id.syncStatusButton)
-        public ImageButton syncStatusButton;
-
-        @BindView(R.id.settingsButton)
-        public ImageButton menuButton;
-
-        private HeaderViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private HeaderViewHolder(SyncedFoldersItemHeaderBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 
     static class FooterViewHolder extends SectionedViewHolder {
-        @BindView(R.id.footer_text)
-        public TextView title;
+        protected SyncedFoldersFooterBinding binding;
 
-        private FooterViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private FooterViewHolder(SyncedFoldersFooterBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 
     static class EmptyViewHolder extends SectionedViewHolder {
-        private EmptyViewHolder(View itemView) {
-            super(itemView);
+        private EmptyViewHolder(SyncedFoldersEmptyBinding binding) {
+            super(binding.getRoot());
         }
     }
 
     static class MainViewHolder extends SectionedViewHolder {
-        @BindView(R.id.thumbnail)
-        public ImageView image;
-
-        @BindView(R.id.counterLayout)
-        public LinearLayout counterBar;
-
-        @BindView(R.id.counter)
-        public TextView counterValue;
-
-        @BindView(R.id.thumbnailDarkener)
-        public ImageView thumbnailDarkener;
+        protected GridSyncItemBinding binding;
 
-        private MainViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private MainViewHolder(GridSyncItemBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 
     private void setSyncButtonActiveIcon(ImageButton syncStatusButton, boolean enabled) {
         if (enabled) {
-            syncStatusButton.setImageDrawable(ThemeDrawableUtils.tintDrawable(R.drawable.ic_cloud_sync_on,
-                                                                              ThemeColorUtils.primaryColor(context, true)));
+            syncStatusButton.setImageDrawable(
+                ThemeDrawableUtils.tintDrawable(
+                    R.drawable.ic_cloud_sync_on,
+                    ThemeColorUtils.primaryColor(context, true)));
         } else {
             syncStatusButton.setImageResource(R.drawable.ic_cloud_sync_off);
         }

+ 28 - 46
src/main/java/com/owncloud/android/ui/adapter/TrashbinListAdapter.java

@@ -27,12 +27,12 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.ListFooterBinding;
+import com.owncloud.android.databinding.TrashbinItemBinding;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
 import com.owncloud.android.lib.common.utils.Log_OC;
@@ -47,8 +47,6 @@ import java.util.List;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR;
 import static com.owncloud.android.datamodel.OCFile.ROOT_PATH;
@@ -105,11 +103,13 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
     @Override
     public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         if (viewType == TRASHBIN_ITEM) {
-            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.trashbin_item, parent, false);
-            return new TrashbinFileViewHolder(v);
+            return new TrashbinFileViewHolder(
+                TrashbinItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         } else {
-            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_footer, parent, false);
-            return new TrashbinFooterViewHolder(v);
+            return new TrashbinFooterViewHolder(
+                ListFooterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         }
     }
 
@@ -120,17 +120,17 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
             TrashbinFile file = files.get(position);
 
             // layout
-            trashbinFileViewHolder.itemLayout.setOnClickListener(v -> trashbinActivityInterface.onItemClicked(file));
+            trashbinFileViewHolder.binding.ListItemLayout.setOnClickListener(v -> trashbinActivityInterface.onItemClicked(file));
 
             // thumbnail
-            trashbinFileViewHolder.thumbnail.setTag(file.getRemoteId());
-            setThumbnail(file, trashbinFileViewHolder.thumbnail);
+            trashbinFileViewHolder.binding.thumbnail.setTag(file.getRemoteId());
+            setThumbnail(file, trashbinFileViewHolder.binding.thumbnail);
 
             // fileName
-            trashbinFileViewHolder.fileName.setText(file.getFileName());
+            trashbinFileViewHolder.binding.Filename.setText(file.getFileName());
 
             // fileSize
-            trashbinFileViewHolder.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
+            trashbinFileViewHolder.binding.fileSize.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));
 
             // originalLocation
             String location;
@@ -140,26 +140,26 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
             } else {
                 location = ROOT_PATH;
             }
-            trashbinFileViewHolder.originalLocation.setText(location);
+            trashbinFileViewHolder.binding.originalLocation.setText(location);
 
             // deletion time
-            trashbinFileViewHolder.deletionTimestamp.setText(DisplayUtils.getRelativeTimestamp(context,
+            trashbinFileViewHolder.binding.deletionTimestamp.setText(DisplayUtils.getRelativeTimestamp(context,
                     file.getDeletionTimestamp() * 1000));
 
             // checkbox
-            trashbinFileViewHolder.checkbox.setVisibility(View.GONE);
+            trashbinFileViewHolder.binding.customCheckbox.setVisibility(View.GONE);
 
             // overflow menu
-            trashbinFileViewHolder.overflowMenu.setOnClickListener(v ->
+            trashbinFileViewHolder.binding.overflowMenu.setOnClickListener(v ->
                     trashbinActivityInterface.onOverflowIconClicked(file, v));
 
             // restore button
-            trashbinFileViewHolder.restoreButton.setOnClickListener(v ->
+            trashbinFileViewHolder.binding.restore.setOnClickListener(v ->
                     trashbinActivityInterface.onRestoreIconClicked(file, v));
 
         } else {
             TrashbinFooterViewHolder trashbinFooterViewHolder = (TrashbinFooterViewHolder) holder;
-            trashbinFooterViewHolder.title.setText(getFooterText());
+            trashbinFooterViewHolder.binding.footerText.setText(getFooterText());
         }
     }
 
@@ -297,39 +297,21 @@ public class TrashbinListAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
     }
 
     public class TrashbinFileViewHolder extends RecyclerView.ViewHolder {
-        @BindView(R.id.thumbnail)
-        public ImageView thumbnail;
-        @BindView(R.id.Filename)
-        public TextView fileName;
-        @BindView(R.id.fileSize)
-        public TextView fileSize;
-        @BindView(R.id.deletionTimestamp)
-        public TextView deletionTimestamp;
-        @BindView(R.id.originalLocation)
-        public TextView originalLocation;
-        @BindView(R.id.restore)
-        public ImageView restoreButton;
-        @BindView(R.id.customCheckbox)
-        public ImageView checkbox;
-        @BindView(R.id.overflowMenu)
-        public ImageView overflowMenu;
-        @BindView(R.id.ListItemLayout)
-        public LinearLayout itemLayout;
-
-        private TrashbinFileViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        protected TrashbinItemBinding binding;
+
+        private TrashbinFileViewHolder(TrashbinItemBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
             // todo action mode
         }
     }
 
     public class TrashbinFooterViewHolder extends RecyclerView.ViewHolder {
-        @BindView(R.id.footerText)
-        public TextView title;
+        protected ListFooterBinding binding;
 
-        private TrashbinFooterViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        private TrashbinFooterViewHolder(ListFooterBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 }

+ 86 - 114
src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java

@@ -35,12 +35,7 @@ import android.text.format.DateUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.PopupMenu;
-import android.widget.ProgressBar;
-import android.widget.TextView;
 
 import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter;
 import com.afollestad.sectionedrecyclerview.SectionedViewHolder;
@@ -52,6 +47,8 @@ import com.nextcloud.client.network.ConnectivityService;
 import com.nextcloud.java.util.Optional;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.UploadListHeaderBinding;
+import com.owncloud.android.databinding.UploadListItemBinding;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
@@ -75,8 +72,6 @@ import java.io.File;
 import java.util.Arrays;
 
 import androidx.annotation.NonNull;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 /**
  * This Adapter populates a ListView with following types of uploads: pending, active, completed. Filtering possible.
@@ -111,25 +106,24 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
 
         UploadGroup group = uploadGroups[section];
 
-        headerViewHolder.title.setText(String.format(parentActivity.getString(R.string.uploads_view_group_header),
-                                                     group.getGroupName(), group.getGroupItemCount()));
-        headerViewHolder.title.setTextColor(ThemeColorUtils.primaryAccentColor(parentActivity));
+        headerViewHolder.binding.uploadListTitle.setText(
+            String.format(parentActivity.getString(R.string.uploads_view_group_header),
+                          group.getGroupName(), group.getGroupItemCount()));
+        headerViewHolder.binding.uploadListTitle.setTextColor(ThemeColorUtils.primaryAccentColor(parentActivity));
 
-        headerViewHolder.title.setOnClickListener(v -> toggleSectionExpanded(section));
+        headerViewHolder.binding.uploadListTitle.setOnClickListener(v -> toggleSectionExpanded(section));
 
         switch (group.type) {
             case CURRENT:
-                headerViewHolder.action.setImageResource(R.drawable.ic_close);
-                break;
             case FINISHED:
-                headerViewHolder.action.setImageResource(R.drawable.ic_close);
+                headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_close);
                 break;
             case FAILED:
-                headerViewHolder.action.setImageResource(R.drawable.ic_sync);
+                headerViewHolder.binding.uploadListAction.setImageResource(R.drawable.ic_sync);
                 break;
         }
 
-        headerViewHolder.action.setOnClickListener(v -> {
+        headerViewHolder.binding.uploadListAction.setOnClickListener(v -> {
             switch (group.type) {
                 case CURRENT:
                     FileUploader.FileUploaderBinder uploaderBinder = parentActivity.getFileUploaderBinder();
@@ -223,7 +217,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
 
         OCUpload item = uploadGroups[section].getItem(relativePosition);
 
-        itemViewHolder.name.setText(item.getLocalPath());
+        itemViewHolder.binding.uploadName.setText(item.getLocalPath());
 
         // local file name
         File remoteFile = new File(item.getRemotePath());
@@ -231,17 +225,17 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
         if (fileName.length() == 0) {
             fileName = File.separator;
         }
-        itemViewHolder.name.setText(fileName);
+        itemViewHolder.binding.uploadName.setText(fileName);
 
         // remote path to parent folder
-        itemViewHolder.remotePath.setText(new File(item.getRemotePath()).getParent());
+        itemViewHolder.binding.uploadRemotePath.setText(new File(item.getRemotePath()).getParent());
 
         // file size
         if (item.getFileSize() != 0) {
-            itemViewHolder.fileSize.setText(String.format("%s, ",
+            itemViewHolder.binding.uploadFileSize.setText(String.format("%s, ",
                     DisplayUtils.bytesToHumanReadable(item.getFileSize())));
         } else {
-            itemViewHolder.fileSize.setText("");
+            itemViewHolder.binding.uploadFileSize.setText("");
         }
 
         // upload date
@@ -250,36 +244,37 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                                                                          updateTime,
                                                                          DateUtils.SECOND_IN_MILLIS,
                                                                          DateUtils.WEEK_IN_MILLIS, 0);
-        itemViewHolder.date.setText(dateString);
+        itemViewHolder.binding.uploadDate.setText(dateString);
 
         // account
         final Optional<User> optionalUser = accountManager.getUser(item.getAccountName());
         if (showUser) {
-            itemViewHolder.account.setVisibility(View.VISIBLE);
+            itemViewHolder.binding.uploadAccount.setVisibility(View.VISIBLE);
             if (optionalUser.isPresent()) {
-                itemViewHolder.account.setText(DisplayUtils.getAccountNameDisplayText(optionalUser.get()));
+                itemViewHolder.binding.uploadAccount.setText(
+                    DisplayUtils.getAccountNameDisplayText(optionalUser.get()));
             } else {
-                itemViewHolder.account.setText(item.getAccountName());
+                itemViewHolder.binding.uploadAccount.setText(item.getAccountName());
             }
         } else {
-            itemViewHolder.account.setVisibility(View.GONE);
+            itemViewHolder.binding.uploadAccount.setVisibility(View.GONE);
         }
 
         // Reset fields visibility
-        itemViewHolder.date.setVisibility(View.VISIBLE);
-        itemViewHolder.remotePath.setVisibility(View.VISIBLE);
-        itemViewHolder.fileSize.setVisibility(View.VISIBLE);
-        itemViewHolder.status.setVisibility(View.VISIBLE);
-        itemViewHolder.progressBar.setVisibility(View.GONE);
+        itemViewHolder.binding.uploadDate.setVisibility(View.VISIBLE);
+        itemViewHolder.binding.uploadRemotePath.setVisibility(View.VISIBLE);
+        itemViewHolder.binding.uploadFileSize.setVisibility(View.VISIBLE);
+        itemViewHolder.binding.uploadStatus.setVisibility(View.VISIBLE);
+        itemViewHolder.binding.uploadProgressBar.setVisibility(View.GONE);
 
         // Update information depending of upload details
         String status = getStatusText(item);
         switch (item.getUploadStatus()) {
             case UPLOAD_IN_PROGRESS:
-                ThemeBarUtils.colorHorizontalProgressBar(itemViewHolder.progressBar,
+                ThemeBarUtils.colorHorizontalProgressBar(itemViewHolder.binding.uploadProgressBar,
                                                          ThemeColorUtils.primaryAccentColor(parentActivity));
-                itemViewHolder.progressBar.setProgress(0);
-                itemViewHolder.progressBar.setVisibility(View.VISIBLE);
+                itemViewHolder.binding.uploadProgressBar.setProgress(0);
+                itemViewHolder.binding.uploadProgressBar.setVisibility(View.VISIBLE);
 
                 FileUploader.FileUploaderBinder binder = parentActivity.getFileUploaderBinder();
                 if (binder != null) {
@@ -293,14 +288,15 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                             );
                         }
                         // ... then, bind the current progress bar to listen for updates
-                        progressListener = new ProgressListener(item, itemViewHolder.progressBar);
+                        progressListener = new ProgressListener(item, itemViewHolder.binding.uploadProgressBar);
                         binder.addDatatransferProgressListener(progressListener, item);
                     } else {
                         // not really uploading; stop listening progress if view is reused!
-                        if (progressListener != null && progressListener.isWrapping(itemViewHolder.progressBar)) {
+                        if (progressListener != null &&
+                            progressListener.isWrapping(itemViewHolder.binding.uploadProgressBar)) {
                             binder.removeDatatransferProgressListener(progressListener,
-                                                                      progressListener.getUpload()   // the one that was added
-                            );
+                                                                      progressListener.getUpload() // the one that was added
+                                                                     );
                             progressListener = null;
                         }
                     }
@@ -308,27 +304,27 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                     Log_OC.w(TAG, "FileUploaderBinder not ready yet for upload " + item.getRemotePath());
                 }
 
-                itemViewHolder.date.setVisibility(View.GONE);
-                itemViewHolder.fileSize.setVisibility(View.GONE);
-                itemViewHolder.progressBar.invalidate();
+                itemViewHolder.binding.uploadDate.setVisibility(View.GONE);
+                itemViewHolder.binding.uploadFileSize.setVisibility(View.GONE);
+                itemViewHolder.binding.uploadProgressBar.invalidate();
                 break;
 
             case UPLOAD_FAILED:
-                itemViewHolder.date.setVisibility(View.GONE);
+                itemViewHolder.binding.uploadDate.setVisibility(View.GONE);
                 break;
 
             case UPLOAD_SUCCEEDED:
-                itemViewHolder.status.setVisibility(View.GONE);
+                itemViewHolder.binding.uploadStatus.setVisibility(View.GONE);
                 break;
         }
-        itemViewHolder.status.setText(status);
+        itemViewHolder.binding.uploadStatus.setText(status);
 
         // bind listeners to perform actions
         if (item.getUploadStatus() == UploadStatus.UPLOAD_IN_PROGRESS) {
             // Cancel
-            itemViewHolder.button.setImageResource(R.drawable.ic_action_cancel_grey);
-            itemViewHolder.button.setVisibility(View.VISIBLE);
-            itemViewHolder.button.setOnClickListener(v -> {
+            itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_action_cancel_grey);
+            itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE);
+            itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> {
                 FileUploader.FileUploaderBinder uploaderBinder = parentActivity.getFileUploaderBinder();
                 if (uploaderBinder != null) {
                     uploaderBinder.cancel(item);
@@ -338,8 +334,8 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
 
         } else if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
             if (item.getLastResult() == UploadResult.SYNC_CONFLICT) {
-                itemViewHolder.button.setImageResource(R.drawable.ic_dots_vertical);
-                itemViewHolder.button.setOnClickListener(view -> {
+                itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_dots_vertical);
+                itemViewHolder.binding.uploadRightButton.setOnClickListener(view -> {
                     if (optionalUser.isPresent()) {
                         User user = optionalUser.get();
                         showItemConflictPopup(user, itemViewHolder, item, status, view);
@@ -347,20 +343,20 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                 });
             } else {
                 // Delete
-                itemViewHolder.button.setImageResource(R.drawable.ic_action_delete_grey);
-                itemViewHolder.button.setOnClickListener(v -> removeUpload(item));
+                itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_action_delete_grey);
+                itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> removeUpload(item));
             }
-            itemViewHolder.button.setVisibility(View.VISIBLE);
+            itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE);
         } else {    // UploadStatus.UPLOAD_SUCCESS
-            itemViewHolder.button.setVisibility(View.INVISIBLE);
+            itemViewHolder.binding.uploadRightButton.setVisibility(View.INVISIBLE);
         }
 
-        itemViewHolder.itemLayout.setOnClickListener(null);
+        itemViewHolder.binding.uploadListItemLayout.setOnClickListener(null);
 
         // click on item
         if (item.getUploadStatus() == UploadStatus.UPLOAD_FAILED) {
             final UploadResult uploadResult = item.getLastResult();
-            itemViewHolder.itemLayout.setOnClickListener(v -> {
+            itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> {
                 if (uploadResult == UploadResult.CREDENTIAL_ERROR) {
                     parentActivity.getFileOperationsHelper().checkCurrentCredentials(item.getAccount(accountManager));
                     return;
@@ -384,11 +380,11 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                 }
             });
         } else {
-            itemViewHolder.itemLayout.setOnClickListener(v -> onUploadItemClick(item));
+            itemViewHolder.binding.uploadListItemLayout.setOnClickListener(v -> onUploadItemClick(item));
         }
 
         // Set icon or thumbnail
-        itemViewHolder.thumbnail.setImageResource(R.drawable.file);
+        itemViewHolder.binding.thumbnail.setImageResource(R.drawable.file);
 
         /*
          * Cancellation needs do be checked and done before changing the drawable in fileIcon, or
@@ -399,7 +395,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
         fakeFileToCheatThumbnailsCacheManagerInterface.setMimeType(item.getMimeType());
 
         boolean allowedToCreateNewThumbnail = ThumbnailsCacheManager.cancelPotentialThumbnailWork(
-                fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.thumbnail
+                fakeFileToCheatThumbnailsCacheManagerInterface, itemViewHolder.binding.thumbnail
         );
 
         // TODO this code is duplicated; refactor to a common place
@@ -411,13 +407,15 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                     String.valueOf(fakeFileToCheatThumbnailsCacheManagerInterface.getRemoteId())
             );
             if (thumbnail != null && !fakeFileToCheatThumbnailsCacheManagerInterface.isUpdateThumbnailNeeded()) {
-                itemViewHolder.thumbnail.setImageBitmap(thumbnail);
+                itemViewHolder.binding.thumbnail.setImageBitmap(thumbnail);
             } else {
                 // generate new Thumbnail
                 if (allowedToCreateNewThumbnail) {
                     final ThumbnailsCacheManager.ThumbnailGenerationTask task =
                             new ThumbnailsCacheManager.ThumbnailGenerationTask(
-                                itemViewHolder.thumbnail, parentActivity.getStorageManager(), parentActivity.getAccount()
+                                itemViewHolder.binding.thumbnail,
+                                parentActivity.getStorageManager(),
+                                parentActivity.getAccount()
                             );
                     if (thumbnail == null) {
                         if (MimeTypeUtil.isVideo(fakeFileToCheatThumbnailsCacheManagerInterface)) {
@@ -432,14 +430,14 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                                 thumbnail,
                                 task
                             );
-                    itemViewHolder.thumbnail.setImageDrawable(asyncDrawable);
+                    itemViewHolder.binding.thumbnail.setImageDrawable(asyncDrawable);
                     task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(
                             fakeFileToCheatThumbnailsCacheManagerInterface, null));
                 }
             }
 
             if ("image/png".equals(item.getMimeType())) {
-                itemViewHolder.thumbnail.setBackgroundColor(parentActivity.getResources()
+                itemViewHolder.binding.thumbnail.setBackgroundColor(parentActivity.getResources()
                         .getColor(R.color.bg_default));
             }
 
@@ -450,12 +448,12 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
             Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
                     String.valueOf(file.hashCode()));
             if (thumbnail != null) {
-                itemViewHolder.thumbnail.setImageBitmap(thumbnail);
+                itemViewHolder.binding.thumbnail.setImageBitmap(thumbnail);
             } else {
                 // generate new Thumbnail
                 if (allowedToCreateNewThumbnail) {
                     final ThumbnailsCacheManager.ThumbnailGenerationTask task =
-                            new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.thumbnail);
+                            new ThumbnailsCacheManager.ThumbnailGenerationTask(itemViewHolder.binding.thumbnail);
 
                     if (MimeTypeUtil.isVideo(file)) {
                         thumbnail = ThumbnailsCacheManager.mDefaultVideo;
@@ -467,14 +465,14 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                         new ThumbnailsCacheManager.AsyncThumbnailDrawable(parentActivity.getResources(), thumbnail,
                                                                           task);
 
-                    itemViewHolder.thumbnail.setImageDrawable(asyncDrawable);
+                    itemViewHolder.binding.thumbnail.setImageDrawable(asyncDrawable);
                     task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file, null));
                     Log_OC.v(TAG, "Executing task to generate a new thumbnail");
                 }
             }
 
             if ("image/png".equalsIgnoreCase(item.getMimeType())) {
-                itemViewHolder.thumbnail.setBackgroundColor(parentActivity.getResources()
+                itemViewHolder.binding.thumbnail.setBackgroundColor(parentActivity.getResources()
                         .getColor(R.color.bg_default));
             }
         } else {
@@ -484,7 +482,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                                                                    fileName,
                                                                    user,
                                                                    parentActivity);
-                itemViewHolder.thumbnail.setImageDrawable(icon);
+                itemViewHolder.binding.thumbnail.setImageDrawable(icon);
             }
         }
     }
@@ -500,7 +498,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
             OCFile folder = storageManager.getFileByPath(new File(remotePath).getParent() + "/");
             if (folder != null && folder.isFolder()) {
                 this.refreshFolder(itemViewHolder, user.toPlatformAccount(), folder, (caller, result) -> {
-                    itemViewHolder.status.setText(status);
+                    itemViewHolder.binding.uploadStatus.setText(status);
                     if (result.isSuccess()) {
                         OCFile file = storageManager.getFileByPath(remotePath);
                         if (file != null) {
@@ -549,9 +547,13 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
         loadUploadItemsFromDb();
     }
 
-    private void refreshFolder(ItemViewHolder view, Account account, OCFile folder, OnRemoteOperationListener listener) {
-        view.itemLayout.setClickable(false);
-        view.status.setText(R.string.uploads_view_upload_status_fetching_server_version);
+    private void refreshFolder(
+        ItemViewHolder view,
+        Account account,
+        OCFile folder,
+        OnRemoteOperationListener listener) {
+        view.binding.uploadListItemLayout.setClickable(false);
+        view.binding.uploadStatus.setText(R.string.uploads_view_upload_status_fetching_server_version);
         Context context = MainApp.getAppContext();
         new RefreshFolderOperation(folder,
                                    clock.getCurrentTime(),
@@ -562,7 +564,7 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
                                    account,
                                    context)
             .execute(account, context, (caller, result) -> {
-                view.itemLayout.setClickable(true);
+                view.binding.uploadListItemLayout.setClickable(true);
                 listener.onRemoteOperationFinish(caller, result);
             }, parentActivity.getHandler());
     }
@@ -707,10 +709,12 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
     public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
         if (viewType == VIEW_TYPE_HEADER) {
             return new HeaderViewHolder(
-                    LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_list_header, parent, false));
+                UploadListHeaderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         } else {
             return new ItemViewHolder(
-                    LayoutInflater.from(parent.getContext()).inflate(R.layout.upload_list_item, parent, false));
+                UploadListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
+            );
         }
     }
 
@@ -757,52 +761,20 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter<SectionedVie
     }
 
     static class HeaderViewHolder extends SectionedViewHolder {
-        @BindView(R.id.upload_list_title)
-        TextView title;
+        UploadListHeaderBinding binding;
 
-        @BindView(R.id.upload_list_action)
-        ImageView action;
-
-        HeaderViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        HeaderViewHolder(UploadListHeaderBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 
     static class ItemViewHolder extends SectionedViewHolder {
-        @BindView(R.id.upload_name)
-        public TextView name;
-
-        @BindView(R.id.thumbnail)
-        public ImageView thumbnail;
-
-        @BindView(R.id.upload_file_size)
-        public TextView fileSize;
-
-        @BindView(R.id.upload_date)
-        public TextView date;
-
-        @BindView(R.id.upload_status)
-        public TextView status;
-
-        @BindView(R.id.upload_account)
-        public TextView account;
-
-        @BindView(R.id.upload_remote_path)
-        public TextView remotePath;
-
-        @BindView(R.id.upload_progress_bar)
-        public ProgressBar progressBar;
-
-        @BindView(R.id.upload_right_button)
-        public  ImageButton button;
-
-        @BindView(R.id.upload_list_item_layout)
-        public LinearLayout itemLayout;
+        UploadListItemBinding binding;
 
-        ItemViewHolder(View itemView) {
-            super(itemView);
-            ButterKnife.bind(this, itemView);
+        ItemViewHolder(UploadListItemBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
         }
     }
 

+ 12 - 18
src/main/java/com/owncloud/android/ui/dialog/LocalStoragePathPickerDialogFragment.java

@@ -20,7 +20,6 @@
 
 package com.owncloud.android.ui.dialog;
 
-import android.annotation.SuppressLint;
 import android.app.Dialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
@@ -29,6 +28,7 @@ import android.view.LayoutInflater;
 import android.view.View;
 
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.StoragePathDialogBinding;
 import com.owncloud.android.ui.adapter.StoragePathAdapter;
 import com.owncloud.android.ui.adapter.StoragePathItem;
 import com.owncloud.android.utils.FileStorageUtils;
@@ -45,10 +45,6 @@ import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.DialogFragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.Unbinder;
 
 /**
  * Picker dialog for choosing a (storage) path.
@@ -66,10 +62,7 @@ public class LocalStoragePathPickerDialogFragment extends DialogFragment
         internalStoragePaths.add("/mnt/sdcard");
     }
 
-    private Unbinder unbinder;
-
-    @BindView(R.id.storage_path_recycler_view)
-    RecyclerView recyclerView;
+    private StoragePathDialogBinding binding;
 
     public static LocalStoragePathPickerDialogFragment newInstance() {
         return new LocalStoragePathPickerDialogFragment();
@@ -81,7 +74,9 @@ public class LocalStoragePathPickerDialogFragment extends DialogFragment
 
         AlertDialog alertDialog = (AlertDialog) getDialog();
 
-        ThemeButtonUtils.themeBorderlessButton(alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE));
+        if (alertDialog != null) {
+            ThemeButtonUtils.themeBorderlessButton(alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE));
+        }
     }
 
     @Override
@@ -99,13 +94,13 @@ public class LocalStoragePathPickerDialogFragment extends DialogFragment
 
         // Inflate the layout for the dialog
         LayoutInflater inflater = requireActivity().getLayoutInflater();
-        @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.storage_path_dialog, null, false);
+        binding = StoragePathDialogBinding.inflate(inflater, null, false);
+        View view = binding.getRoot();
 
         StoragePathAdapter adapter = new StoragePathAdapter(getPathList(), this);
 
-        unbinder = ButterKnife.bind(this, view);
-        recyclerView.setAdapter(adapter);
-        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+        binding.storagePathRecyclerView.setAdapter(adapter);
+        binding.storagePathRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
 
         // Build the dialog
         AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
@@ -117,10 +112,9 @@ public class LocalStoragePathPickerDialogFragment extends DialogFragment
     }
 
     @Override
-    public void onStop() {
-        unbinder.unbind();
-
-        super.onStop();
+    public void onDestroyView() {
+        binding = null;
+        super.onDestroyView();
     }
 
     @Override

+ 13 - 13
src/main/java/com/owncloud/android/ui/dialog/MultipleAccountsDialog.java

@@ -26,7 +26,6 @@
 
 package com.owncloud.android.ui.dialog;
 
-import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.Dialog;
 import android.os.Bundle;
@@ -39,6 +38,7 @@ import com.nextcloud.client.account.User;
 import com.nextcloud.client.account.UserAccountManager;
 import com.nextcloud.client.di.Injectable;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.MultipleAccountsBinding;
 import com.owncloud.android.ui.activity.ReceiveExternalFilesActivity;
 import com.owncloud.android.ui.adapter.UserListAdapter;
 import com.owncloud.android.ui.adapter.UserListItem;
@@ -52,13 +52,9 @@ import androidx.annotation.NonNull;
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.DialogFragment;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 
 public class MultipleAccountsDialog extends DialogFragment implements Injectable, UserListAdapter.ClickListener {
-    @BindView(R.id.list)
-    RecyclerView listView;
+    private MultipleAccountsBinding binding;
 
     @Inject UserAccountManager accountManager;
 
@@ -72,9 +68,7 @@ public class MultipleAccountsDialog extends DialogFragment implements Injectable
 
         // Inflate the layout for the dialog
         LayoutInflater inflater = activity.getLayoutInflater();
-        @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.multiple_accounts, null);
-        ButterKnife.bind(this, view);
-
+        binding = MultipleAccountsBinding.inflate(inflater, null, false);
 
         final ReceiveExternalFilesActivity parent = (ReceiveExternalFilesActivity) getActivity();
         AlertDialog.Builder builder = new AlertDialog.Builder(parent);
@@ -86,11 +80,11 @@ public class MultipleAccountsDialog extends DialogFragment implements Injectable
                                                       false,
                                                       false);
 
-        listView.setHasFixedSize(true);
-        listView.setLayoutManager(new LinearLayoutManager(activity));
-        listView.setAdapter(adapter);
+        binding.list.setHasFixedSize(true);
+        binding.list.setLayoutManager(new LinearLayoutManager(activity));
+        binding.list.setAdapter(adapter);
 
-        builder.setView(view).setTitle(R.string.common_choose_account);
+        builder.setView(binding.getRoot()).setTitle(R.string.common_choose_account);
         Dialog dialog = builder.create();
 
         Window window = dialog.getWindow();
@@ -132,4 +126,10 @@ public class MultipleAccountsDialog extends DialogFragment implements Injectable
         }
         dismiss();
     }
+
+    @Override
+    public void onDestroyView() {
+        binding = null;
+        super.onDestroyView();
+    }
 }

+ 31 - 43
src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java

@@ -34,15 +34,14 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CompoundButton;
 import android.widget.DatePicker;
-import android.widget.TextView;
 
-import com.google.android.material.button.MaterialButton;
 import com.google.android.material.snackbar.Snackbar;
 import com.nextcloud.client.account.User;
 import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.jobs.BackgroundJobManager;
 import com.nextcloud.java.util.Optional;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.ContactsBackupFragmentBinding;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
@@ -71,11 +70,7 @@ import javax.inject.Inject;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.widget.SwitchCompat;
 import androidx.fragment.app.Fragment;
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.OnClick;
 import third_parties.daveKoeller.AlphanumComparator;
 
 import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP;
@@ -89,17 +84,7 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
     private static final String KEY_CALENDAR_MONTH = "CALENDAR_MONTH";
     private static final String KEY_CALENDAR_YEAR = "CALENDAR_YEAR";
 
-    @BindView(R.id.contacts_automatic_backup)
-    public SwitchCompat backupSwitch;
-
-    @BindView(R.id.contacts_datepicker)
-    public MaterialButton contactsDatePickerBtn;
-
-    @BindView(R.id.contacts_last_backup_timestamp)
-    public TextView lastBackup;
-
-    @BindView(R.id.contacts_backup_now)
-    public MaterialButton backupNow;
+    private ContactsBackupFragmentBinding binding;
 
     @Inject BackgroundJobManager backgroundJobManager;
 
@@ -128,8 +113,9 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
         if (ThemeUtils.themingEnabled(getContext())) {
             getContext().getTheme().applyStyle(R.style.FallbackThemingTheme, true);
         }
-        View view = inflater.inflate(R.layout.contacts_backup_fragment, null);
-        ButterKnife.bind(this, view);
+
+        binding = ContactsBackupFragmentBinding.inflate(inflater, container, false);
+        View view = binding.getRoot();
 
         setHasOptionsMenu(true);
 
@@ -153,31 +139,29 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
 
         arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver());
 
-        ThemeCheckableUtils.tintSwitch(backupSwitch, ThemeColorUtils.primaryAccentColor(getContext()));
-        backupSwitch.setChecked(arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP));
+        ThemeCheckableUtils.tintSwitch(
+            binding.contactsAutomaticBackup, ThemeColorUtils.primaryAccentColor(getContext()));
+        binding.contactsAutomaticBackup.setChecked(
+            arbitraryDataProvider.getBooleanValue(user, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP));
 
-        onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                if (checkAndAskForContactsReadPermission()) {
-                    if (isChecked) {
-                        setAutomaticBackup(true);
-                    } else {
-                        setAutomaticBackup(false);
-                    }
-                }
+        onCheckedChangeListener = (buttonView, isChecked) -> {
+            if (checkAndAskForContactsReadPermission()) {
+                setAutomaticBackup(isChecked);
             }
         };
 
-        backupSwitch.setOnCheckedChangeListener(onCheckedChangeListener);
+        binding.contactsAutomaticBackup.setOnCheckedChangeListener(onCheckedChangeListener);
+        binding.contactsBackupNow.setOnClickListener(v -> backupContacts());
+        binding.contactsDatepicker.setOnClickListener(v -> openCleanDate());
 
         // display last backup
         Long lastBackupTimestamp = arbitraryDataProvider.getLongValue(user, PREFERENCE_CONTACTS_LAST_BACKUP);
 
         if (lastBackupTimestamp == -1) {
-            lastBackup.setText(R.string.contacts_preference_backup_never);
+            binding.contactsLastBackupTimestamp.setText(R.string.contacts_preference_backup_never);
         } else {
-            lastBackup.setText(DisplayUtils.getRelativeTimestamp(contactsPreferenceActivity, lastBackupTimestamp));
+            binding.contactsLastBackupTimestamp.setText(
+                DisplayUtils.getRelativeTimestamp(contactsPreferenceActivity, lastBackupTimestamp));
         }
 
         if (savedInstanceState != null && savedInstanceState.getBoolean(KEY_CALENDAR_PICKER_OPEN, false)) {
@@ -190,8 +174,8 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
             calendarPickerOpen = true;
         }
 
-        ThemeButtonUtils.colorPrimaryButton(backupNow, getContext());
-        ThemeButtonUtils.colorPrimaryButton(contactsDatePickerBtn, getContext());
+        ThemeButtonUtils.colorPrimaryButton(binding.contactsBackupNow, getContext());
+        ThemeButtonUtils.colorPrimaryButton(binding.contactsDatepicker, getContext());
 
         return view;
     }
@@ -250,9 +234,9 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
                     Collections.sort(backupFiles, new AlphanumComparator<>());
 
                     if (backupFiles == null || backupFiles.isEmpty()) {
-                        contactsDatePickerBtn.setVisibility(View.GONE);
+                        binding.contactsDatepicker.setVisibility(View.GONE);
                     } else {
-                        contactsDatePickerBtn.setVisibility(View.VISIBLE);
+                        binding.contactsDatepicker.setVisibility(View.VISIBLE);
                     }
                 }
             }
@@ -299,9 +283,9 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
                     if (grantResults[index] >= 0) {
                         setAutomaticBackup(true);
                     } else {
-                        backupSwitch.setOnCheckedChangeListener(null);
-                        backupSwitch.setChecked(false);
-                        backupSwitch.setOnCheckedChangeListener(onCheckedChangeListener);
+                        binding.contactsAutomaticBackup.setOnCheckedChangeListener(null);
+                        binding.contactsAutomaticBackup.setChecked(false);
+                        binding.contactsAutomaticBackup.setOnCheckedChangeListener(onCheckedChangeListener);
                     }
 
                     break;
@@ -322,7 +306,6 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
         }
     }
 
-    @OnClick(R.id.contacts_backup_now)
     public void backupContacts() {
         if (checkAndAskForContactsReadPermission()) {
             startContactsBackupJob();
@@ -395,7 +378,6 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
         }
     }
 
-    @OnClick(R.id.contacts_datepicker)
     public void openCleanDate() {
         openDate(null);
     }
@@ -467,6 +449,12 @@ public class ContactsBackupFragment extends FileFragment implements DatePickerDi
         }
     }
 
+    @Override
+    public void onDestroyView() {
+        binding = null;
+        super.onDestroyView();
+    }
+
     @Override
     public void onStop() {
         super.onStop();