Эх сурвалжийг харах

Integrate Dagger 2

Closes #3751

Signed-off-by: Chris Narkiewicz <hello@ezaquarii.com>
Chris Narkiewicz 6 жил өмнө
parent
commit
8fbe2ee38e

+ 6 - 0
build.gradle

@@ -277,6 +277,12 @@ dependencies {
     findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.8.0'
     findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.3'
 
+    implementation 'com.google.dagger:dagger:2.21'
+    implementation 'com.google.dagger:dagger-android:2.21'
+    implementation 'com.google.dagger:dagger-android-support:2.21'
+    annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
+    annotationProcessor 'com.google.dagger:dagger-android-processor:2.21'
+
 //    jacocoAnt "org.jacoco:org.jacoco.ant:${jacocoVersion}"
 //    jacocoAgent "org.jacoco:org.jacoco.agent:${jacocoVersion}"
 //    androidJacocoAgent "org.jacoco:org.jacoco.agent:${jacocoVersion}"

+ 12 - 0
findbugs-filter.xml

@@ -8,6 +8,18 @@
     <Match>
         <Class name="~.*\.R\$.*"/>
     </Match>
+    <Match>
+        <Class name="~.*\.R\$.*"/>
+    </Match>
+
+    <!-- Dagger code is autogenerated. Exclude it from Check. -->
+    <Match>
+        <Or>
+            <Class name="~.*\.Dagger.*"/>
+            <Class name="~com.nextcloud.client.di\..*_.*"/>
+        </Or>
+
+    </Match>
     <Bug pattern="PATH_TRAVERSAL_IN" />
     <Bug pattern="ANDROID_EXTERNAL_FILE_ACCESS" />
 </FindBugsFilter>

+ 75 - 0
src/main/java/com/nextcloud/client/di/ActivityInjector.java

@@ -0,0 +1,75 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+import android.app.Activity;
+import android.app.Application;
+import android.os.Bundle;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import dagger.android.AndroidInjection;
+
+public class ActivityInjector implements Application.ActivityLifecycleCallbacks {
+
+    @Override
+    public final void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+        if (activity instanceof Injectable) {
+            AndroidInjection.inject(activity);
+        }
+
+        if (activity instanceof FragmentActivity) {
+            final FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager();
+            fm.registerFragmentLifecycleCallbacks(new FragmentInjector(), true);
+        }
+    }
+
+    @Override
+    public final void onActivityStarted(Activity activity) {
+        // not needed
+    }
+
+    @Override
+    public final void onActivityResumed(Activity activity) {
+        // not needed
+    }
+
+    @Override
+    public final void onActivityPaused(Activity activity) {
+        // not needed
+    }
+
+    @Override
+    public final void onActivityStopped(Activity activity) {
+        // not needed
+    }
+
+    @Override
+    public final void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+        // not needed
+    }
+
+    @Override
+    public final void onActivityDestroyed(Activity activity) {
+        // not needed
+    }
+}
+

+ 45 - 0
src/main/java/com/nextcloud/client/di/AppComponent.java

@@ -0,0 +1,45 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+import android.app.Application;
+
+import com.owncloud.android.MainApp;
+
+import dagger.BindsInstance;
+import dagger.Component;
+import dagger.android.support.AndroidSupportInjectionModule;
+
+@Component(modules = {
+    AndroidSupportInjectionModule.class,
+    AppModule.class
+})
+public interface AppComponent {
+    void inject(MainApp app);
+
+    @Component.Builder
+    interface Builder {
+        @BindsInstance
+        Builder application(Application application);
+
+        AppComponent build();
+    }
+}

+ 44 - 0
src/main/java/com/nextcloud/client/di/AppModule.java

@@ -0,0 +1,44 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+import android.app.Application;
+import android.content.Context;
+
+import com.nextcloud.client.preferences.AppPreferences;
+import com.nextcloud.client.preferences.PreferenceManager;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module(includes = {ComponentsModule.class})
+class AppModule {
+
+    @Provides
+    Context context(Application application) {
+        return application.getApplicationContext();
+    }
+
+    @Provides
+    AppPreferences preferences(Application application) {
+        return PreferenceManager.fromContext(application);
+    }
+}

+ 40 - 0
src/main/java/com/nextcloud/client/di/ComponentsModule.java

@@ -0,0 +1,40 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+
+import dagger.Module;
+import dagger.android.ContributesAndroidInjector;
+
+/**
+ * Register classes that require dependency injection. This class is used by Dagger compiler
+ * only.
+ */
+@Module
+abstract class ComponentsModule {
+    @ContributesAndroidInjector
+    abstract FileDisplayActivity fileDisplayActivity();
+
+    @ContributesAndroidInjector
+    abstract FileDetailFragment fileDetailFragment();
+}

+ 47 - 0
src/main/java/com/nextcloud/client/di/FragmentInjector.java

@@ -0,0 +1,47 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import dagger.android.support.AndroidSupportInjection;
+
+class FragmentInjector extends FragmentManager.FragmentLifecycleCallbacks {
+    @Override
+    public void onFragmentPreAttached(
+        @NonNull FragmentManager fragmentManager,
+        @NonNull Fragment fragment,
+        @NonNull Context context
+    ) {
+        super.onFragmentPreAttached(fragmentManager, fragment, context);
+        if (fragment instanceof Injectable) {
+            try {
+                AndroidSupportInjection.inject(fragment);
+            } catch (IllegalArgumentException directCause) {
+                // this provides a cause description that is a bit more friendly for developers
+                throw new InjectorNotFoundException(fragment, directCause);
+            }
+        }
+    }
+}

+ 35 - 0
src/main/java/com/nextcloud/client/di/Injectable.java

@@ -0,0 +1,35 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+/**
+ * Marks object as injectable by {@link DependencyInjector}.
+ * <p>
+ * Any {@link android.app.Activity} or {@link androidx.fragment.app.Fragment} implementing
+ * this interface will be automatically supplied with dependencies by {@link DependencyInjector}.
+ * <p>
+ * Activities are considered fully-initialized after call to {@link android.app.Activity#onCreate(Bundle)}
+ * (this means after {@code super.onCreate(savedStateInstance)} returns).
+ * <p>
+ * Injectable Fragments are supplied with dependencies before {@link androidx.fragment.app.Fragment#onAttach(Context)}.
+ */
+public interface Injectable {
+}

+ 34 - 0
src/main/java/com/nextcloud/client/di/InjectorNotFoundException.java

@@ -0,0 +1,34 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.client.di;
+
+class InjectorNotFoundException extends RuntimeException {
+    InjectorNotFoundException(Object object, Throwable cause) {
+        super(
+            String.format(
+                "Injector not registered for %s. Have you added it to %s?",
+                object.getClass().getName(),
+                ComponentsModule.class.getName()
+            ),
+            cause
+        );
+    }
+}

+ 35 - 0
src/main/java/com/nextcloud/client/di/package-info.java

@@ -0,0 +1,35 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Chris Narkiewicz
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * This package contains application Dependency Injection code, based on Dagger 2.
+ * <p>
+ * To enable dependency injection for a component, such as {@link android.app.Activity},
+ * {@link androidx.fragment.app.Fragment} or {@link android.app.Service}, the component must be
+ * first registered in {@link com.nextcloud.client.di.ComponentsModule} class.
+ * <p>
+ * {@link com.nextcloud.client.di.ComponentsModule} will be used by Dagger compiler to
+ * create an injector for a given class.
+ *
+ * @see com.nextcloud.client.di.InjectorNotFoundException
+ * @see dagger.android.AndroidInjection
+ * @see dagger.android.support.AndroidSupportInjection
+ */
+package com.nextcloud.client.di;

+ 50 - 2
src/main/java/com/owncloud/android/MainApp.java

@@ -25,6 +25,7 @@ import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.app.Service;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -41,9 +42,12 @@ import androidx.annotation.RequiresApi;
 import androidx.annotation.StringRes;
 import androidx.appcompat.app.AlertDialog;
 import androidx.core.util.Pair;
+import androidx.fragment.app.Fragment;
 import androidx.multidex.MultiDexApplication;
 import com.evernote.android.job.JobManager;
 import com.evernote.android.job.JobRequest;
+import com.nextcloud.client.di.ActivityInjector;
+import com.nextcloud.client.di.DaggerAppComponent;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.nextcloud.client.preferences.PreferenceManager;
 import com.owncloud.android.authentication.AccountUtils;
@@ -73,8 +77,14 @@ import com.owncloud.android.utils.FilesSyncHelper;
 import com.owncloud.android.utils.PermissionUtil;
 import com.owncloud.android.utils.ReceiversHelper;
 import com.owncloud.android.utils.SecurityUtils;
+import dagger.android.AndroidInjector;
+import dagger.android.DispatchingAndroidInjector;
+import dagger.android.HasActivityInjector;
+import dagger.android.HasServiceInjector;
+import dagger.android.support.HasSupportFragmentInjector;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
+import javax.inject.Inject;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -91,7 +101,10 @@ import static com.owncloud.android.ui.activity.ContactsPreferenceActivity.PREFER
  * Contains methods to build the "static" strings. These strings were before constants in different
  * classes
  */
-public class MainApp extends MultiDexApplication {
+public class MainApp extends MultiDexApplication implements
+    HasActivityInjector,
+    HasSupportFragmentInjector,
+    HasServiceInjector {
 
     public static final OwnCloudVersion OUTDATED_SERVER_VERSION = OwnCloudVersion.nextcloud_14;
     public static final OwnCloudVersion MINIMUM_SUPPORTED_SERVER_VERSION = OwnCloudVersion.nextcloud_12;
@@ -112,7 +125,19 @@ public class MainApp extends MultiDexApplication {
     private static boolean mOnlyOnDevice;
 
     private SharedPreferences sharedPreferences;
-    private AppPreferences preferences;
+
+    @Inject
+    AppPreferences preferences;
+
+    @Inject
+    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
+
+    @Inject
+    DispatchingAndroidInjector<Fragment> dispatchingFragmentInjector;
+
+    @Inject
+    DispatchingAndroidInjector<Service> dispatchingServiceInjector;
+
     private PassCodeManager passCodeManager;
 
     @SuppressWarnings("unused")
@@ -122,6 +147,14 @@ public class MainApp extends MultiDexApplication {
     @Override
     public void onCreate() {
         super.onCreate();
+
+        DaggerAppComponent.builder()
+            .application(this)
+            .build()
+            .inject(this);
+
+        registerActivityLifecycleCallbacks(new ActivityInjector());
+
         JobManager.create(this).addJobCreator(new NCJobCreator());
         MainApp.mContext = getApplicationContext();
 
@@ -657,4 +690,19 @@ public class MainApp extends MultiDexApplication {
             }
         }
     }
+
+    @Override
+    public AndroidInjector<Activity> activityInjector() {
+        return dispatchingActivityInjector;
+    }
+
+    @Override
+    public AndroidInjector<Fragment> supportFragmentInjector() {
+        return dispatchingFragmentInjector;
+    }
+
+    @Override
+    public AndroidInjector<Service> serviceInjector() {
+        return dispatchingServiceInjector;
+    }
 }

+ 6 - 4
src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -56,6 +56,7 @@ import android.widget.ImageView;
 
 import com.google.android.material.bottomnavigation.BottomNavigationView;
 import com.google.android.material.snackbar.Snackbar;
+import com.nextcloud.client.di.Injectable;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.ArbitraryDataProvider;
@@ -132,6 +133,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import javax.inject.Inject;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.StringRes;
 import androidx.appcompat.app.AlertDialog;
@@ -149,7 +152,7 @@ import androidx.fragment.app.FragmentTransaction;
 public class FileDisplayActivity extends HookActivity
         implements FileFragment.ContainerActivity,
         OnEnforceableRefreshListener, SortingOrderDialogFragment.OnSortingOrderListener,
-        SendShareDialog.SendShareDialogDownloader {
+        SendShareDialog.SendShareDialogDownloader, Injectable {
 
     public static final String RESTART = "RESTART";
 
@@ -206,7 +209,8 @@ public class FileDisplayActivity extends HookActivity
     private boolean searchOpen;
 
     private SearchView searchView;
-    private AppPreferences preferences;
+    @Inject
+    AppPreferences preferences;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -216,8 +220,6 @@ public class FileDisplayActivity extends HookActivity
         super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account
         // is valid
 
-        preferences = PreferenceManager.fromContext(this);
-
         /// Load of saved instance state
         if (savedInstanceState != null) {
             mWaitingToPreview = savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);

+ 5 - 9
src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

@@ -107,6 +107,7 @@ import java.io.FileWriter;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Method;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -151,7 +152,7 @@ public class ReceiveExternalFilesActivity extends FileActivity
     private String mSubjectText;
     private String mExtraText;
 
-    private final static String FILENAME_ENCODING = "UTF-8";
+    private final static Charset FILENAME_ENCODING = Charset.forName("UTF-8");
 
     private LinearLayout mEmptyListContainer;
     private TextView mEmptyListMessage;
@@ -575,14 +576,9 @@ public class ReceiveExternalFilesActivity extends FileActivity
             safeFilename = safeFilename.replaceAll("=", "_");
             safeFilename = safeFilename.replaceAll(",", "_");
 
-            try {
-                int maxLength = 128;
-                if (safeFilename.getBytes(FILENAME_ENCODING).length > maxLength) {
-                    safeFilename = new String(safeFilename.getBytes(FILENAME_ENCODING), 0, maxLength, FILENAME_ENCODING);
-                }
-            } catch (UnsupportedEncodingException e) {
-                Log_OC.e(TAG, "rename failed ", e);
-                return null;
+            int maxLength = 128;
+            if (safeFilename.getBytes(FILENAME_ENCODING).length > maxLength) {
+                safeFilename = new String(safeFilename.getBytes(FILENAME_ENCODING), 0, maxLength, FILENAME_ENCODING);
             }
             return safeFilename;
         }

+ 7 - 2
src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -41,6 +41,7 @@ import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import com.google.android.material.tabs.TabLayout;
+import com.nextcloud.client.di.Injectable;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
@@ -65,6 +66,8 @@ import com.owncloud.android.utils.ThemeUtils;
 
 import java.lang.ref.WeakReference;
 
+import javax.inject.Inject;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.viewpager.widget.ViewPager;
@@ -75,7 +78,7 @@ import butterknife.Unbinder;
 /**
  * This Fragment is used to display the details about a file.
  */
-public class FileDetailFragment extends FileFragment implements OnClickListener {
+public class FileDetailFragment extends FileFragment implements OnClickListener, Injectable {
     private static final String TAG = FileDetailFragment.class.getSimpleName();
     private static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
     static final String FTAG_RENAME_FILE = "RENAME_FILE_FRAGMENT";
@@ -141,7 +144,9 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
     private ProgressListener progressListener;
     private ToolbarActivity activity;
     private int activeTab;
-    private AppPreferences preferences;
+
+    @Inject
+    AppPreferences preferences;
 
     /**
      * Public factory method to create new FileDetailFragment instances.