diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index ce37ca33f512..99f788ba5c57 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -127,7 +127,6 @@ import com.duckduckgo.app.tabs.ui.TabSwitcherActivity import com.duckduckgo.mobile.android.ui.DuckDuckGoTheme import com.duckduckgo.mobile.android.ui.store.ThemingDataStore import com.google.android.material.snackbar.Snackbar -import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_browser_tab.* import kotlinx.android.synthetic.main.include_cta_buttons.view.* import kotlinx.android.synthetic.main.include_dax_dialog_cta.* @@ -180,6 +179,7 @@ import com.duckduckgo.app.downloads.DownloadsFileActions import com.duckduckgo.app.browser.menu.BrowserPopupMenu import com.duckduckgo.app.browser.print.PrintInjector import com.duckduckgo.app.browser.remotemessage.asMessage +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.app.global.FragmentViewModelFactory import com.duckduckgo.app.global.view.launchDefaultAppActivity import com.duckduckgo.app.playstore.PlayStoreUtils @@ -208,7 +208,7 @@ import javax.inject.Provider @InjectWith(FragmentScope::class) class BrowserTabFragment : - Fragment(), + DuckDuckGoFragment(), FindListener, CoroutineScope, DaxDialogListener, @@ -466,11 +466,6 @@ class BrowserTabFragment : private val pulseAnimation: PulseAnimation = PulseAnimation(this) - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) removeDaxDialogFromActivity() diff --git a/app/src/main/java/com/duckduckgo/app/email/ui/EmailProtectionFragment.kt b/app/src/main/java/com/duckduckgo/app/email/ui/EmailProtectionFragment.kt index fe14a3567aca..772f86c0ce87 100644 --- a/app/src/main/java/com/duckduckgo/app/email/ui/EmailProtectionFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/email/ui/EmailProtectionFragment.kt @@ -16,27 +16,20 @@ package com.duckduckgo.app.email.ui -import android.content.Context import android.os.Bundle import android.view.View import androidx.annotation.LayoutRes -import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.app.global.FragmentViewModelFactory -import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -abstract class EmailProtectionFragment(@LayoutRes contentLayoutId: Int = 0) : Fragment(contentLayoutId) { +abstract class EmailProtectionFragment(@LayoutRes contentLayoutId: Int = 0) : DuckDuckGoFragment(contentLayoutId) { @Inject lateinit var viewModelFactory: FragmentViewModelFactory - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) configureUi() diff --git a/app/src/main/java/com/duckduckgo/app/feedback/ui/common/FeedbackFragment.kt b/app/src/main/java/com/duckduckgo/app/feedback/ui/common/FeedbackFragment.kt index e1499c31704a..a9271ebf61da 100644 --- a/app/src/main/java/com/duckduckgo/app/feedback/ui/common/FeedbackFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/feedback/ui/common/FeedbackFragment.kt @@ -16,26 +16,19 @@ package com.duckduckgo.app.feedback.ui.common -import android.content.Context import android.os.Bundle import androidx.annotation.LayoutRes -import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.app.global.FragmentViewModelFactory -import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -abstract class FeedbackFragment(@LayoutRes contentLayoutId: Int = 0) : Fragment(contentLayoutId) { +abstract class FeedbackFragment(@LayoutRes contentLayoutId: Int = 0) : DuckDuckGoFragment(contentLayoutId) { @Inject lateinit var viewModelFactory: FragmentViewModelFactory - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) configureListeners() diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/DefaultBrowserPage.kt b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/DefaultBrowserPage.kt index be7b8bdaf324..10b0c0e89fc3 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/DefaultBrowserPage.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/DefaultBrowserPage.kt @@ -18,7 +18,6 @@ package com.duckduckgo.app.onboarding.ui.page import android.annotation.SuppressLint import android.content.ActivityNotFoundException -import android.content.Context import android.content.Intent import android.graphics.Color import android.net.Uri @@ -40,7 +39,6 @@ import com.duckduckgo.mobile.android.ui.view.show import com.duckduckgo.app.statistics.VariantManager import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.di.scopes.FragmentScope -import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.content_onboarding_default_browser.* import kotlinx.android.synthetic.main.include_default_browser_buttons.* import timber.log.Timber @@ -69,11 +67,6 @@ class DefaultBrowserPage : OnboardingPageFragment() { override fun layoutResource(): Int = R.layout.content_onboarding_default_browser - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun setUserVisibleHint(isVisibleToUser: Boolean) { super.setUserVisibleHint(isVisibleToUser) if (isVisibleToUser) { diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/OnboardingPageFragment.kt b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/OnboardingPageFragment.kt index 3db28f73d828..19d6cdbb6811 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/OnboardingPageFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/OnboardingPageFragment.kt @@ -21,10 +21,10 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.LayoutRes -import androidx.fragment.app.Fragment +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.app.onboarding.ui.OnboardingActivity -abstract class OnboardingPageFragment : Fragment() { +abstract class OnboardingPageFragment : DuckDuckGoFragment() { @LayoutRes abstract fun layoutResource(): Int diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePage.kt b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePage.kt index 6ffec388167d..de0c540aceb1 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePage.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePage.kt @@ -17,7 +17,6 @@ package com.duckduckgo.app.onboarding.ui.page import android.app.Activity.RESULT_OK -import android.content.Context import android.content.Intent import android.graphics.Color import android.os.Bundle @@ -34,7 +33,6 @@ import com.duckduckgo.mobile.android.R as CommonR import com.duckduckgo.app.global.extensions.html import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.di.scopes.FragmentScope -import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.content_onboarding_welcome.* import kotlinx.android.synthetic.main.include_dax_dialog_cta.* import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -84,11 +82,6 @@ class WelcomePage : OnboardingPageFragment() { } } - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - private fun render(state: WelcomePageView.State) { when (state) { WelcomePageView.State.Idle -> {} diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementCredentialsMode.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementCredentialsMode.kt index ee1efe16ad05..7095f997bf28 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementCredentialsMode.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementCredentialsMode.kt @@ -16,10 +16,8 @@ package com.duckduckgo.autofill.ui.credential.management.viewing -import android.content.Context import android.graphics.drawable.BitmapDrawable import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -35,6 +33,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.favicon.FaviconManager +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.app.global.FragmentViewModelFactory import com.duckduckgo.autofill.domain.app.LoginCredentials import com.duckduckgo.autofill.impl.R @@ -44,12 +43,11 @@ import com.duckduckgo.autofill.ui.credential.management.AutofillSettingsViewMode import com.duckduckgo.autofill.ui.credential.management.AutofillSettingsViewModel.CredentialMode.Viewing import com.duckduckgo.di.scopes.FragmentScope import com.duckduckgo.mobile.android.ui.view.OutLinedTextInputView -import dagger.android.support.AndroidSupportInjection import kotlinx.coroutines.launch import javax.inject.Inject @InjectWith(FragmentScope::class) -class AutofillManagementCredentialsMode : Fragment(), MenuProvider { +class AutofillManagementCredentialsMode : DuckDuckGoFragment(), MenuProvider { @Inject lateinit var faviconManager: FaviconManager @@ -69,11 +67,6 @@ class AutofillManagementCredentialsMode : Fragment(), MenuProvider { private lateinit var binding: FragmentAutofillManagementEditModeBinding - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementDisabledMode.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementDisabledMode.kt index aa425300f719..f2fc270efa90 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementDisabledMode.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementDisabledMode.kt @@ -17,34 +17,27 @@ package com.duckduckgo.autofill.ui.credential.management.viewing import android.annotation.SuppressLint -import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import com.duckduckgo.anvil.annotations.InjectWith +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.autofill.impl.databinding.FragmentAutofillManagementDisabledBinding import com.duckduckgo.di.scopes.FragmentScope -import dagger.android.support.AndroidSupportInjection import javax.inject.Inject @InjectWith(FragmentScope::class) -class AutofillManagementDisabledMode : Fragment() { +class AutofillManagementDisabledMode : DuckDuckGoFragment() { @Inject lateinit var appBuildConfig: AppBuildConfig private lateinit var binding: FragmentAutofillManagementDisabledBinding - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementListMode.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementListMode.kt index 8e94beab0503..8e9af518483d 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementListMode.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementListMode.kt @@ -16,9 +16,7 @@ package com.duckduckgo.autofill.ui.credential.management.viewing -import android.content.Context import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -29,6 +27,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.favicon.FaviconManager +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.app.global.FragmentViewModelFactory import com.duckduckgo.autofill.domain.app.LoginCredentials import com.duckduckgo.autofill.impl.databinding.FragmentAutofillManagementListModeBinding @@ -38,13 +37,12 @@ import com.duckduckgo.autofill.ui.credential.management.sorting.CredentialGroupe import com.duckduckgo.autofill.ui.credential.management.LoginCredentialTitleExtractor import com.duckduckgo.di.scopes.FragmentScope import com.duckduckgo.mobile.android.ui.view.quietlySetIsChecked -import dagger.android.support.AndroidSupportInjection import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @InjectWith(FragmentScope::class) -class AutofillManagementListMode : Fragment() { +class AutofillManagementListMode : DuckDuckGoFragment() { @Inject lateinit var faviconManager: FaviconManager @@ -72,11 +70,6 @@ class AutofillManagementListMode : Fragment() { } } - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = FragmentAutofillManagementListModeBinding.inflate(inflater, container, false) configureToggle() diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementLockedMode.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementLockedMode.kt index a0ac15d52fec..fd800a9d5798 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementLockedMode.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/ui/credential/management/viewing/AutofillManagementLockedMode.kt @@ -16,6 +16,7 @@ package com.duckduckgo.autofill.ui.credential.management.viewing +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -23,6 +24,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import com.duckduckgo.autofill.impl.databinding.FragmentAutofillManagementLockedBinding +@SuppressLint("NoFragment") // we don't use DI here class AutofillManagementLockedMode : Fragment() { private lateinit var binding: FragmentAutofillManagementLockedBinding diff --git a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/system/ComponentCardsFragment.kt b/common-ui/src/main/java/com/duckduckgo/app/global/DuckDuckGoFragment.kt similarity index 55% rename from common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/system/ComponentCardsFragment.kt rename to common-ui/src/main/java/com/duckduckgo/app/global/DuckDuckGoFragment.kt index ccc2a807daa6..d50c20c71acb 100644 --- a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/system/ComponentCardsFragment.kt +++ b/common-ui/src/main/java/com/duckduckgo/app/global/DuckDuckGoFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 DuckDuckGo + * Copyright (c) 2022 DuckDuckGo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,17 @@ * limitations under the License. */ -package com.duckduckgo.mobile.android.themepreview.ui.component.system +package com.duckduckgo.app.global -import com.duckduckgo.mobile.android.themepreview.ui.component.Component -import com.duckduckgo.mobile.android.themepreview.ui.component.ComponentFragment +import android.content.Context +import androidx.annotation.LayoutRes +import dagger.android.DaggerFragment +import dagger.android.support.AndroidSupportInjection -class ComponentCardsFragment : ComponentFragment() { - override fun getComponents(): List { - return listOf(Component.SNACKBAR) +abstract class DuckDuckGoFragment(@LayoutRes contentLayoutId: Int = 0) : DaggerFragment(contentLayoutId) { + + override fun onAttach(context: Context) { + AndroidSupportInjection.inject(this) + super.onAttach(context) } } diff --git a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/ComponentFragment.kt b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/ComponentFragment.kt index 7d55fcafccfd..47336818156c 100644 --- a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/ComponentFragment.kt +++ b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/component/ComponentFragment.kt @@ -16,6 +16,7 @@ package com.duckduckgo.mobile.android.themepreview.ui.component +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -25,6 +26,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.duckduckgo.mobile.android.R +@SuppressLint("NoFragment") // we don't use DI here abstract class ComponentFragment : Fragment() { override fun onCreateView( diff --git a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/dialogs/DialogsFragment.kt b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/dialogs/DialogsFragment.kt index 3a04bcb3e790..6257545e12e9 100644 --- a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/dialogs/DialogsFragment.kt +++ b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/dialogs/DialogsFragment.kt @@ -16,6 +16,7 @@ package com.duckduckgo.mobile.android.themepreview.ui.dialogs +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -26,6 +27,7 @@ import com.duckduckgo.mobile.android.R import com.duckduckgo.mobile.android.ui.view.TypewriterDaxDialog /** Fragment to display a list of dialogs. */ +@SuppressLint("NoFragment") // we don't use DI here class DialogsFragment : Fragment() { override fun onCreateView( diff --git a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/palette/ColorPaletteFragment.kt b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/palette/ColorPaletteFragment.kt index 2ad9e36a7715..07040ce08e1d 100644 --- a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/palette/ColorPaletteFragment.kt +++ b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/palette/ColorPaletteFragment.kt @@ -16,6 +16,7 @@ package com.duckduckgo.mobile.android.themepreview.ui.palette +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -24,6 +25,7 @@ import androidx.fragment.app.Fragment import com.duckduckgo.mobile.android.R /** Fragment to display a list of subsystems that show the values of this app's theme. */ +@SuppressLint("NoFragment") // we don't use DI here class ColorPaletteFragment : Fragment() { override fun onCreateView( diff --git a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/typography/TypographyFragment.kt b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/typography/TypographyFragment.kt index 13042a699095..3377771a1a48 100644 --- a/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/typography/TypographyFragment.kt +++ b/common-ui/src/main/java/com/duckduckgo/mobile/android/themepreview/ui/typography/TypographyFragment.kt @@ -16,6 +16,7 @@ package com.duckduckgo.mobile.android.themepreview.ui.typography +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -24,6 +25,7 @@ import androidx.fragment.app.Fragment import com.duckduckgo.mobile.android.R /** Fragment to display a list of subsystems that show the values of this app's theme. */ +@SuppressLint("NoFragment") // we don't use DI here class TypographyFragment : Fragment() { override fun onCreateView( diff --git a/di/src/main/java/dagger/android/DaggerActivity.kt b/di/src/main/java/dagger/android/DaggerActivity.kt index 029b51f22989..393bbf32d44a 100644 --- a/di/src/main/java/dagger/android/DaggerActivity.kt +++ b/di/src/main/java/dagger/android/DaggerActivity.kt @@ -29,8 +29,7 @@ abstract class DaggerActivity : AppCompatActivity(), HasDaggerInjector { ?: throw RuntimeException( """ Could not find the dagger component for ${key.simpleName}. - You probably forgot to create the ${key.simpleName}Component. - If you DID create the ${key.simpleName}Component, check that it uses @ContributesTo(ActivityScope::class) + You probably forgot to annotate your class with @InjectWith(Scope::class). """.trimIndent() ) } diff --git a/di/src/main/java/dagger/android/DaggerFragment.kt b/di/src/main/java/dagger/android/DaggerFragment.kt new file mode 100644 index 000000000000..64042bee152f --- /dev/null +++ b/di/src/main/java/dagger/android/DaggerFragment.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dagger.android + +import android.annotation.SuppressLint +import androidx.annotation.LayoutRes +import androidx.fragment.app.Fragment +import com.duckduckgo.di.DaggerMap +import javax.inject.Inject + +@SuppressLint("NoFragment") // this is base fragment class to be used instead of Fragment +abstract class DaggerFragment(@LayoutRes contentLayoutId: Int = 0) : Fragment(contentLayoutId), HasDaggerInjector { + @Inject + lateinit var injectorFactoryMap: DaggerMap, AndroidInjector.Factory<*>> + + override fun daggerFactoryFor(key: Class<*>): AndroidInjector.Factory<*> { + return injectorFactoryMap[key] + ?: throw RuntimeException( + """ + Could not find the dagger component for ${key.simpleName}. + You probably forgot to annotate your class with @InjectWith(Scope::class). + """.trimIndent() + ) + } +} diff --git a/lint-rules/src/main/java/com/duckduckgo/lint/DuckDuckGoIssueRegistry.kt b/lint-rules/src/main/java/com/duckduckgo/lint/DuckDuckGoIssueRegistry.kt index c8438cc115c4..f3420eff2075 100644 --- a/lint-rules/src/main/java/com/duckduckgo/lint/DuckDuckGoIssueRegistry.kt +++ b/lint-rules/src/main/java/com/duckduckgo/lint/DuckDuckGoIssueRegistry.kt @@ -20,6 +20,7 @@ import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor import com.android.tools.lint.detector.api.CURRENT_API import com.android.tools.lint.detector.api.Issue +import com.duckduckgo.lint.NoFragmentDetector.Companion.NO_FRAGMENT_ISSUE import com.duckduckgo.lint.NoLifecycleObserverDetector.Companion.NO_LIFECYCLE_OBSERVER_ISSUE import com.duckduckgo.lint.NoSingletonDetector.Companion.NO_SINGLETON_ISSUE @@ -29,6 +30,7 @@ class DuckDuckGoIssueRegistry : IssueRegistry() { get() = listOf( NO_SINGLETON_ISSUE, NO_LIFECYCLE_OBSERVER_ISSUE, + NO_FRAGMENT_ISSUE, ) override val api: Int diff --git a/lint-rules/src/main/java/com/duckduckgo/lint/NoFragmentDetector.kt b/lint-rules/src/main/java/com/duckduckgo/lint/NoFragmentDetector.kt new file mode 100644 index 000000000000..c0f853443c35 --- /dev/null +++ b/lint-rules/src/main/java/com/duckduckgo/lint/NoFragmentDetector.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.lint + +import com.android.tools.lint.client.api.UElementHandler +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope.JAVA_FILE +import com.android.tools.lint.detector.api.Scope.TEST_SOURCES +import com.android.tools.lint.detector.api.Severity.ERROR +import com.android.tools.lint.detector.api.SourceCodeScanner +import org.jetbrains.uast.UClass +import java.util.* + +@Suppress("UnstableApiUsage") +class NoFragmentDetector : Detector(), SourceCodeScanner { + override fun getApplicableUastTypes() = listOf(UClass::class.java) + + override fun createUastHandler(context: JavaContext): UElementHandler = NoInternalImportHandler(context) + + internal class NoInternalImportHandler(private val context: JavaContext) : UElementHandler() { + override fun visitClass(node: UClass) { + if (node.extendsListTypes.any { it.className == "Fragment" }) { + context.report(NO_FRAGMENT_ISSUE, node, context.getNameLocation(node), "Fragment should not be directly extended") + } + } + } + + companion object { + val NO_FRAGMENT_ISSUE = Issue.create("NoFragment", + "The Fragment type should not be extended. Use DuckDuckGoFragment instead.", + """ + The Fragment should not be used. + Use DuckDuckGoFragment instead. + """.trimIndent(), + Category.CORRECTNESS, 10, ERROR, + Implementation(NoFragmentDetector::class.java, EnumSet.of(JAVA_FILE, TEST_SOURCES)) + ) + } +} diff --git a/lint-rules/src/test/java/com/duckduckgo/lint/NoFragmentDetectorTest.kt b/lint-rules/src/test/java/com/duckduckgo/lint/NoFragmentDetectorTest.kt new file mode 100644 index 000000000000..00ffe5294648 --- /dev/null +++ b/lint-rules/src/test/java/com/duckduckgo/lint/NoFragmentDetectorTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2022 DuckDuckGo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.duckduckgo.lint + +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.duckduckgo.lint.NoFragmentDetector.Companion.NO_FRAGMENT_ISSUE +import org.junit.Test + +class NoFragmentDetectorTest { + + @Test + fun whenFragmentExtendedThenFailWithError() { + TestLintTask.lint() + .files(TestFiles.kt(""" + package com.duckduckgo.lint + + class Fragment + + class Duck : Fragment() { + fun quack() { + } + } + """).indented()) + .issues(NO_FRAGMENT_ISSUE) + .run() + .expect(""" + src/com/duckduckgo/lint/Fragment.kt:5: Error: Fragment should not be directly extended [NoFragment] + class Duck : Fragment() { + ~~~~ + 1 errors, 0 warnings + """.trimMargin()) + } + + @Test + fun whenFragmentWithParamtExtendedThenFailWithError() { + TestLintTask.lint() + .files(TestFiles.kt(""" + package com.duckduckgo.lint + + class Fragment(val param: String) + + class Duck(param: String) : Fragment(param) { + fun quack() { + } + } + """).indented()) + .issues(NO_FRAGMENT_ISSUE) + .run() + .expect(""" + src/com/duckduckgo/lint/Fragment.kt:5: Error: Fragment should not be directly extended [NoFragment] + class Duck(param: String) : Fragment(param) { + ~~~~ + 1 errors, 0 warnings + """.trimMargin()) + } + + @Test + fun whenAnyOtherFragmentTypeExtendedThenSuccess() { + TestLintTask.lint() + .files(TestFiles.kt(""" + package com.duckduckgo.lint + + class DialogFragment(val param: String) + + class Duck(param: String) : DialogFragment(param) { + fun quack() { + } + } + """).indented()) + .allowCompilationErrors() + .issues(NoLifecycleObserverDetector.NO_LIFECYCLE_OBSERVER_ISSUE) + .run() + .expectClean() + } +} \ No newline at end of file diff --git a/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldFragment.kt b/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldFragment.kt index 15f3a9ea3cdf..30506e08b072 100644 --- a/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldFragment.kt +++ b/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldFragment.kt @@ -27,10 +27,10 @@ import android.widget.ImageView import android.widget.TextView import androidx.core.os.postDelayed import androidx.core.text.HtmlCompat -import androidx.fragment.app.Fragment import androidx.lifecycle.* import com.duckduckgo.app.global.FragmentViewModelFactory import com.duckduckgo.anvil.annotations.InjectWith +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.di.scopes.FragmentScope import com.duckduckgo.mobile.android.ui.view.gone import com.duckduckgo.mobile.android.ui.view.show @@ -40,13 +40,12 @@ import com.duckduckgo.mobile.android.vpn.pixels.DeviceShieldPixels import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.REVOKED import com.duckduckgo.mobile.android.vpn.ui.report.PrivacyReportViewModel.PrivacyReportView.ViewState import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.DeviceShieldTrackerActivity -import dagger.android.support.AndroidSupportInjection import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import javax.inject.Inject @InjectWith(FragmentScope::class) -class DeviceShieldFragment : Fragment() { +class DeviceShieldFragment : DuckDuckGoFragment() { @Inject lateinit var viewModelFactory: FragmentViewModelFactory @@ -68,7 +67,6 @@ class DeviceShieldFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - AndroidSupportInjection.inject(this) val view = inflater.inflate(R.layout.fragment_device_shield_cta, container, false) configureViewReferences(view) diff --git a/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedFragment.kt b/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedFragment.kt index 01391294c2b0..f905118afd38 100644 --- a/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedFragment.kt +++ b/vpn/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedFragment.kt @@ -21,22 +21,21 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import androidx.lifecycle.* import androidx.recyclerview.widget.RecyclerView import com.duckduckgo.app.global.FragmentViewModelFactory import com.duckduckgo.anvil.annotations.InjectWith +import com.duckduckgo.app.global.DuckDuckGoFragment import com.duckduckgo.di.scopes.FragmentScope import com.duckduckgo.mobile.android.ui.recyclerviewext.StickyHeadersLinearLayoutManager import com.duckduckgo.mobile.android.vpn.R import com.duckduckgo.mobile.android.vpn.stats.AppTrackerBlockingStatsRepository.TimeWindow -import dagger.android.AndroidInjection import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit import javax.inject.Inject @InjectWith(FragmentScope::class) -class DeviceShieldActivityFeedFragment : Fragment() { +class DeviceShieldActivityFeedFragment : DuckDuckGoFragment() { @Inject lateinit var viewModelFactory: FragmentViewModelFactory @@ -62,7 +61,6 @@ class DeviceShieldActivityFeedFragment : Fragment() { view: View, savedInstanceState: Bundle? ) { - AndroidInjection.inject(this) with(view.findViewById(R.id.activity_recycler_view)) { val stickyHeadersLayoutManager = StickyHeadersLinearLayoutManager(this@DeviceShieldActivityFeedFragment.requireContext())