From d465110740f203ba2656e68fe94ac365fa4f47ed Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 16 Jul 2025 16:31:10 +0100 Subject: [PATCH 01/16] Remove Kotlinter --- .githooks/pre-commit | 1 - build.gradle | 1 - code-formatting.gradle | 9 --------- versions.properties | 2 -- 4 files changed, 13 deletions(-) diff --git a/.githooks/pre-commit b/.githooks/pre-commit index e4fdce27070d..12719f1fa0a1 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -2,6 +2,5 @@ #!/bin/bash echo "Running spotless check" ./gradlew spotlessApply - ./gradlew formatKotlin git add `git diff --name-only --cached` exit 0 diff --git a/build.gradle b/build.gradle index a620d932d4d2..f77c98963794 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,6 @@ plugins { id 'org.jetbrains.dokka' id 'com.osacky.fulladle' id 'org.jetbrains.kotlin.android' apply false - id 'org.jmailen.kotlinter' apply false id 'com.google.devtools.ksp' apply false id 'com.diffplug.spotless' apply false } diff --git a/code-formatting.gradle b/code-formatting.gradle index 6e4fdbc1a6d8..225372445714 100644 --- a/code-formatting.gradle +++ b/code-formatting.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.diffplug.spotless' -apply plugin: 'org.jmailen.kotlinter' spotless { java { @@ -16,11 +15,3 @@ spotless { trimTrailingWhitespace() } } - -kotlinter { - disabledRules = ['no-wildcard-imports', 'filename', 'package-name', 'annotation-spacing'] -} - -tasks.register('code_format_checks') { - dependsOn 'spotlessCheck', 'lintKotlin' -} diff --git a/versions.properties b/versions.properties index ee2fb678c824..47005d56da2e 100644 --- a/versions.properties +++ b/versions.properties @@ -17,8 +17,6 @@ plugin.com.osacky.fulladle=0.17.4 plugin.org.jetbrains.kotlin=1.9.24 -plugin.org.jmailen.kotlinter=3.12.0 - plugin.com.google.devtools.ksp=1.9.24-1.0.20 plugin.com.diffplug.spotless=6.1.2 From 3baadf2290afec3a45aba5e2a7bffa92444f6cef Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 16 Jul 2025 16:33:03 +0100 Subject: [PATCH 02/16] Update spotless to latest version --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index 47005d56da2e..481a693132a8 100644 --- a/versions.properties +++ b/versions.properties @@ -19,7 +19,7 @@ plugin.org.jetbrains.kotlin=1.9.24 plugin.com.google.devtools.ksp=1.9.24-1.0.20 -plugin.com.diffplug.spotless=6.1.2 +plugin.com.diffplug.spotless=7.1.0 version.android.billingclient=7.1.1 From bb86754a031c9153d17fd6814fba0601bb1ecdbf Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 16 Jul 2025 16:34:55 +0100 Subject: [PATCH 03/16] Add Kotlin formatting to Spotless ratchetFrom means Spotless will apply only to files which have changed since origin/main It also means we need to make some changes to CI due to ensure builds pass due to shallow clones. See https://github.com/diffplug/spotless/tree/main/plugin-gradle#using-ratchetfrom-on-ci-systems for more details. --- .editorconfig | 4 ++++ .github/workflows/ci.yml | 3 ++- .github/workflows/nightly.yml | 3 ++- code-formatting.gradle | 7 +++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index efce1d8a8a87..7a82f7c4eb36 100644 --- a/.editorconfig +++ b/.editorconfig @@ -91,3 +91,7 @@ ij_kotlin_while_on_new_line = false ij_kotlin_wrap_elvis_expressions = 1 ij_kotlin_wrap_expression_body_functions = 1 ij_kotlin_wrap_first_method_in_call_chain = false +ktlint_standard_no-wildcard-imports = disabled +ktlint_standard_filename = disabled +ktlint_standard_package-name = disabled +ktlint_standard_annotation = disabled diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b55b82aa3706..98947505ffcc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: + fetch-depth: 0 # required due to setting Spotless ratchetFrom submodules: recursive - name: Set up JDK version @@ -39,7 +40,7 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: Run Code Formatting Checks - run: ./gradlew code_format_checks + run: ./gradlew spotlessCheck unit_tests: name: Unit tests diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a6b5efc71e5a..ae05a351d4a9 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -18,6 +18,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: + fetch-depth: 0 # required due to setting Spotless ratchetFrom submodules: recursive - name: Set up JDK version @@ -30,7 +31,7 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: Run Code Formatting Checks - run: ./gradlew code_format_checks + run: ./gradlew spotlessCheck unit_tests: name: Unit tests diff --git a/code-formatting.gradle b/code-formatting.gradle index 225372445714..b0535bc4cea7 100644 --- a/code-formatting.gradle +++ b/code-formatting.gradle @@ -1,6 +1,13 @@ apply plugin: 'com.diffplug.spotless' spotless { + ratchetFrom 'origin/develop' + kotlin { + target("src/**/*.kt") + ktlint("1.7.0") + trimTrailingWhitespace() + indentWithSpaces() + } java { target 'src/*/java/**/*.java' googleJavaFormat('1.22.0').aosp() From 7d8d26e214da4645a9c5a5a13c941fe963c973f7 Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 16 Jul 2025 16:49:32 +0100 Subject: [PATCH 04/16] Remove deprecated indentWithSpaces --- code-formatting.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code-formatting.gradle b/code-formatting.gradle index b0535bc4cea7..f941107d6c15 100644 --- a/code-formatting.gradle +++ b/code-formatting.gradle @@ -6,7 +6,7 @@ spotless { target("src/**/*.kt") ktlint("1.7.0") trimTrailingWhitespace() - indentWithSpaces() + leadingTabsToSpaces() } java { target 'src/*/java/**/*.java' @@ -14,11 +14,11 @@ spotless { targetExclude("**/FragmentStateAdapter.java") // temporary removeUnusedImports() trimTrailingWhitespace() - indentWithSpaces() + leadingTabsToSpaces() } format 'xml', { target '**/*.xml' - indentWithSpaces() + leadingTabsToSpaces() trimTrailingWhitespace() } } From 248ceaddb2c9229c2f310d8d9bc3c0c4192fc970 Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 16 Jul 2025 16:35:05 +0100 Subject: [PATCH 05/16] Update documentation --- CONTRIBUTING.md | 3 +-- STYLEGUIDE.md | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ba9fe4ef84c..7c4b98aef9ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,6 +50,5 @@ If your PR is failing because of that, please make sure that you follow our [sty You can also trigger an automatic code formatting of the code by executing: ``` -./gradleW code_format_checks -./gradleW formatKotlin +./gradleW app:spotlessApply ``` diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 1149d370a5af..a32fb8905ada 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -5,8 +5,8 @@ ### Code formatting -You can check the code formatting correctness by running `./gradleW code_format_checks`. -To adhere to codestyle, please run `./gradleW formatKotlin` and `./gradleW spotlessApply` to autoformat in order to fix any CI issues. +You can check the code formatting correctness by running `./gradleW spotlessCheck`. +To adhere to codestyle, please run `./gradleW spotlessApply` to autoformat and fix any CI issues. If you want to do this automatically upon commit we recommend the existing [pre-commit hook](.githooks/pre-commit): - Pull develop branch From 24ef937c891c656ceb374d95ef222c1ee117bcff Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 16 Jul 2025 18:52:33 +0100 Subject: [PATCH 06/16] Fix ignored files committed Spotless breaks because we have classes and files that have been commited that should technically be ignored by our .gitignore settings. See https://github.com/diffplug/spotless/issues/911#issuecomment-904887074 Therefore we have to either remove these files or move them. In this case the DeviceShield stuff was in a package called "report" which we ignore in .gitignore. Therefore I gave it a new package name We want to keep the icon and I guess we want to keep the copyright folder so I made that work in .gitignore --- .gitignore | 5 +++-- .../vpn-impl/src/main/AndroidManifest.xml | 2 +- .../newtab/AppTrackingProtectionStateView.kt | 6 +++--- .../DeviceShieldAppTrackersInfo.kt | 4 ++-- .../DeviceShieldFragment.kt | 9 +++++---- .../PrivacyReportViewModel.kt | 20 ++++++++++--------- .../DeviceShieldTrackerActivity.kt | 2 +- .../res/layout/activity_app_trackers_info.xml | 2 +- .../vpn/ui/PrivacyReportViewModelTest.kt | 2 +- 9 files changed, 28 insertions(+), 24 deletions(-) rename app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/{report => privacyreport}/DeviceShieldAppTrackersInfo.kt (95%) rename app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/{report => privacyreport}/DeviceShieldFragment.kt (95%) rename app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/{report => privacyreport}/PrivacyReportViewModel.kt (80%) diff --git a/.gitignore b/.gitignore index fe3087c55bb1..8bb8d8797bcb 100644 --- a/.gitignore +++ b/.gitignore @@ -41,9 +41,10 @@ captures/ # Intellij *.iml -.idea/ -.idea/workspace.xml +.idea/* !.idea/icon.svg +!.idea/copyright/ +.idea/workspace.xml # Keystore files *.jks diff --git a/app-tracking-protection/vpn-impl/src/main/AndroidManifest.xml b/app-tracking-protection/vpn-impl/src/main/AndroidManifest.xml index 4f14a9aea91b..eae0b3d96127 100644 --- a/app-tracking-protection/vpn-impl/src/main/AndroidManifest.xml +++ b/app-tracking-protection/vpn-impl/src/main/AndroidManifest.xml @@ -28,7 +28,7 @@ android:screenOrientation="portrait" /> - PrivacyReportView.ViewState(vpnState, trackersBlocked, shouldShowCTA()) + ViewState(vpnState, trackersBlocked, shouldShowCTA()) } @VisibleForTesting - fun getReport(): Flow { + fun getReport(): Flow { return repository.getVpnTrackers({ dateOfLastHour() }).map { trackers -> if (trackers.isEmpty()) { - PrivacyReportView.TrackersBlocked("", 0, 0) + TrackersBlocked("", 0, 0) } else { val perApp = trackers.groupBy { it.trackingApp }.toList().sortedByDescending { it.second.sumOf { t -> t.count } } val otherAppsSize = (perApp.size - 1).coerceAtLeast(0) val latestApp = perApp.first().first.appDisplayName - PrivacyReportView.TrackersBlocked(latestApp, otherAppsSize, trackers.sumOf { it.count }) + TrackersBlocked(latestApp, otherAppsSize, trackers.sumOf { it.count }) } } } @@ -74,9 +76,9 @@ class PrivacyReportViewModel @Inject constructor( object PrivacyReportView { data class ViewState( - val vpnState: VpnState, - val trackersBlocked: TrackersBlocked, - val isFeatureEnabled: Boolean, + val vpnState: VpnState, + val trackersBlocked: TrackersBlocked, + val isFeatureEnabled: Boolean, ) data class TrackersBlocked( diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt index 864e05d0e7d8..4b8280950e88 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt @@ -68,7 +68,7 @@ import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnState import com.duckduckgo.mobile.android.vpn.ui.AppBreakageCategory import com.duckduckgo.mobile.android.vpn.ui.alwayson.AlwaysOnAlertDialogFragment -import com.duckduckgo.mobile.android.vpn.ui.report.DeviceShieldAppTrackersInfo +import com.duckduckgo.mobile.android.vpn.ui.privacyreport.DeviceShieldAppTrackersInfo import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.DeviceShieldTrackerActivityViewModel.ViewEvent import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.DeviceShieldTrackerActivityViewModel.ViewEvent.StartVpn import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.view.DisableVpnDialogOptions diff --git a/app-tracking-protection/vpn-impl/src/main/res/layout/activity_app_trackers_info.xml b/app-tracking-protection/vpn-impl/src/main/res/layout/activity_app_trackers_info.xml index 7dca0b2c6bc6..24909105fe02 100644 --- a/app-tracking-protection/vpn-impl/src/main/res/layout/activity_app_trackers_info.xml +++ b/app-tracking-protection/vpn-impl/src/main/res/layout/activity_app_trackers_info.xml @@ -24,7 +24,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".ui.report.DeviceShieldAppTrackersInfo"> + tools:context=".ui.privacyreport.DeviceShieldAppTrackersInfo"> Date: Wed, 16 Jul 2025 18:53:35 +0100 Subject: [PATCH 07/16] Format the classes edited due to moving package changes As these have "changed" since develop then they need formatting! --- .../newtab/AppTrackingProtectionStateView.kt | 109 ++++++------ .../DeviceShieldAppTrackersInfo.kt | 6 +- .../ui/privacyreport/DeviceShieldFragment.kt | 89 +++++----- .../privacyreport/PrivacyReportViewModel.kt | 26 ++- .../DeviceShieldTrackerActivity.kt | 161 +++++++++--------- .../vpn/ui/PrivacyReportViewModelTest.kt | 90 +++++----- 6 files changed, 247 insertions(+), 234 deletions(-) diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt index 2f3c5b737539..86cf1fabf10a 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt @@ -46,10 +46,10 @@ import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.DeviceShieldTracker import com.duckduckgo.newtabpage.api.NewTabPageSection import com.duckduckgo.newtabpage.api.NewTabPageSectionPlugin import dagger.android.support.AndroidSupportInjection -import javax.inject.Inject import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import javax.inject.Inject @InjectWith(ViewScope::class) class AppTrackingProtectionStateView @JvmOverloads constructor( @@ -57,10 +57,7 @@ class AppTrackingProtectionStateView @JvmOverloads constructor( attrs: AttributeSet? = null, defStyle: Int = 0, ) : FrameLayout(context, attrs, defStyle) { - - override fun getTag(): String { - return "AppTP" - } + override fun getTag(): String = "AppTP" @Inject lateinit var deviceShieldPixels: DeviceShieldPixels @@ -83,9 +80,10 @@ class AppTrackingProtectionStateView @JvmOverloads constructor( AndroidSupportInjection.inject(this) super.onAttachedToWindow() - conflatedJob += viewModel.viewStateFlow - .onEach { viewState -> renderViewState(viewState) } - .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) + conflatedJob += + viewModel.viewStateFlow + .onEach { viewState -> renderViewState(viewState) } + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) deviceShieldPixels.didShowNewTabSummary() @@ -141,49 +139,56 @@ class AppTrackingProtectionStateView @JvmOverloads constructor( val lastTrackingApp = trackerBlocked.latestApp val otherApps = trackerBlocked.otherAppsSize - val textToStyle = if (trackersBlocked == 1) { - when (otherApps) { - 0 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeZeroOtherApps, - trackersBlocked, - lastTrackingApp, - ) - - 1 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeOneOtherApp, - trackersBlocked, - lastTrackingApp, - ) - - else -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeMoreOtherApps, - trackersBlocked, - lastTrackingApp, - otherApps, - ) + val textToStyle = + if (trackersBlocked == 1) { + when (otherApps) { + 0 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeZeroOtherApps, + trackersBlocked, + lastTrackingApp, + ) + + 1 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeOneOtherApp, + trackersBlocked, + lastTrackingApp, + ) + + else -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeMoreOtherApps, + trackersBlocked, + lastTrackingApp, + otherApps, + ) + } + } else { + when (otherApps) { + 0 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesZeroOtherApps, + trackersBlocked, + lastTrackingApp, + ) + + 1 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesOneOtherApp, + trackersBlocked, + lastTrackingApp, + ) + + else -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesMoreOtherApps, + trackersBlocked, + lastTrackingApp, + otherApps, + ) + } } - } else { - when (otherApps) { - 0 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesZeroOtherApps, - trackersBlocked, - lastTrackingApp, - ) - - 1 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesOneOtherApp, - trackersBlocked, - lastTrackingApp, - ) - - else -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesMoreOtherApps, - trackersBlocked, - lastTrackingApp, - otherApps, - ) - } - } binding.deviceShieldCtaHeader.text = HtmlCompat.fromHtml(textToStyle, HtmlCompat.FROM_HTML_MODE_LEGACY) binding.deviceShieldCtaImage.setImageResource(R.drawable.ic_apptp_default) @@ -202,9 +207,7 @@ class AppTrackingProtectionNewTabPageSectionPlugin @Inject constructor( ) : NewTabPageSectionPlugin { override val name = NewTabPageSection.APP_TRACKING_PROTECTION.name - override fun getView(context: Context): View { - return AppTrackingProtectionStateView(context) - } + override fun getView(context: Context): View = AppTrackingProtectionStateView(context) override suspend fun isUserEnabled(): Boolean { if (vpnFeatureRemover.isFeatureRemoved()) { diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt index 728c3d21fe72..c840a4f6bbfc 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt @@ -29,7 +29,6 @@ import javax.inject.Inject @InjectWith(ActivityScope::class) class DeviceShieldAppTrackersInfo : DuckDuckGoActivity() { - @Inject lateinit var deviceShieldPixels: DeviceShieldPixels @@ -55,9 +54,6 @@ class DeviceShieldAppTrackersInfo : DuckDuckGoActivity() { } companion object { - - internal fun intent(context: Context): Intent { - return Intent(context, DeviceShieldAppTrackersInfo::class.java) - } + internal fun intent(context: Context): Intent = Intent(context, DeviceShieldAppTrackersInfo::class.java) } } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt index 72a2caf05929..fdcbe3ae605d 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt @@ -41,13 +41,12 @@ import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.REV import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel.PrivacyReportView.TrackersBlocked import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel.PrivacyReportView.ViewState import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.DeviceShieldTrackerActivity -import javax.inject.Inject import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import javax.inject.Inject @InjectWith(FragmentScope::class) class DeviceShieldFragment : DuckDuckGoFragment() { - @Inject lateinit var viewModelFactory: FragmentViewModelFactory @@ -58,8 +57,7 @@ class DeviceShieldFragment : DuckDuckGoFragment() { private lateinit var deviceShieldCtaHeaderTextView: TextView private lateinit var deviceShieldCtaImageView: ImageView - private inline fun bindViewModel() = - lazy { ViewModelProvider(this, viewModelFactory).get(V::class.java) } + private inline fun bindViewModel() = lazy { ViewModelProvider(this, viewModelFactory).get(V::class.java) } private val viewModel: PrivacyReportViewModel by bindViewModel() @@ -145,45 +143,52 @@ class DeviceShieldFragment : DuckDuckGoFragment() { val lastTrackingApp = trackerBlocked.latestApp val otherApps = trackerBlocked.otherAppsSize - val textToStyle = if (trackersBlocked == 1) { - when (otherApps) { - 0 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeZeroOtherApps, - trackersBlocked, - lastTrackingApp, - ) - 1 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeOneOtherApp, - trackersBlocked, - lastTrackingApp, - ) - else -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeMoreOtherApps, - trackersBlocked, - lastTrackingApp, - otherApps, - ) + val textToStyle = + if (trackersBlocked == 1) { + when (otherApps) { + 0 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeZeroOtherApps, + trackersBlocked, + lastTrackingApp, + ) + 1 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeOneOtherApp, + trackersBlocked, + lastTrackingApp, + ) + else -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOneTimeMoreOtherApps, + trackersBlocked, + lastTrackingApp, + otherApps, + ) + } + } else { + when (otherApps) { + 0 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesZeroOtherApps, + trackersBlocked, + lastTrackingApp, + ) + 1 -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesOneOtherApp, + trackersBlocked, + lastTrackingApp, + ) + else -> + resources.getString( + R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesMoreOtherApps, + trackersBlocked, + lastTrackingApp, + otherApps, + ) + } } - } else { - when (otherApps) { - 0 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesZeroOtherApps, - trackersBlocked, - lastTrackingApp, - ) - 1 -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesOneOtherApp, - trackersBlocked, - lastTrackingApp, - ) - else -> resources.getString( - R.string.atp_DailyLastCompanyBlockedHomeTabOtherTimesMoreOtherApps, - trackersBlocked, - lastTrackingApp, - otherApps, - ) - } - } deviceShieldCtaHeaderTextView.text = HtmlCompat.fromHtml(textToStyle, HtmlCompat.FROM_HTML_MODE_LEGACY) deviceShieldCtaImageView.setImageResource(R.drawable.ic_apptp_default) diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt index f0e93c3bdd4b..654aa807d3e1 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt @@ -30,11 +30,11 @@ import com.duckduckgo.mobile.android.vpn.stats.AppTrackerBlockingStatsRepository import com.duckduckgo.mobile.android.vpn.ui.onboarding.VpnStore import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel.PrivacyReportView.TrackersBlocked import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel.PrivacyReportView.ViewState -import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext +import javax.inject.Inject @ContributesViewModel(ViewScope::class) class PrivacyReportViewModel @Inject constructor( @@ -44,14 +44,14 @@ class PrivacyReportViewModel @Inject constructor( vpnStateMonitor: VpnStateMonitor, private val dispatchers: DispatcherProvider, ) : ViewModel() { - - val viewStateFlow = vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN).combine(getReport()) { vpnState, trackersBlocked -> - ViewState(vpnState, trackersBlocked, shouldShowCTA()) - } + val viewStateFlow = + vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN).combine(getReport()) { vpnState, trackersBlocked -> + ViewState(vpnState, trackersBlocked, shouldShowCTA()) + } @VisibleForTesting - fun getReport(): Flow { - return repository.getVpnTrackers({ dateOfLastHour() }).map { trackers -> + fun getReport(): Flow = + repository.getVpnTrackers({ dateOfLastHour() }).map { trackers -> if (trackers.isEmpty()) { TrackersBlocked("", 0, 0) } else { @@ -62,23 +62,21 @@ class PrivacyReportViewModel @Inject constructor( TrackersBlocked(latestApp, otherAppsSize, trackers.sumOf { it.count }) } } - } - private suspend fun shouldShowCTA(): Boolean { - return withContext(dispatchers.io()) { + private suspend fun shouldShowCTA(): Boolean = + withContext(dispatchers.io()) { if (vpnFeatureRemover.isFeatureRemoved()) { false } else { vpnStore.didShowOnboarding() } } - } object PrivacyReportView { data class ViewState( - val vpnState: VpnState, - val trackersBlocked: TrackersBlocked, - val isFeatureEnabled: Boolean, + val vpnState: VpnState, + val trackersBlocked: TrackersBlocked, + val isFeatureEnabled: Boolean, ) data class TrackersBlocked( diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt index 4b8280950e88..df1987345559 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt @@ -76,9 +76,6 @@ import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.view.message.AppTPS import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.view.message.AppTPStateMessagePlugin.DefaultAppTPMessageAction import com.duckduckgo.navigation.api.GlobalActivityStarter import com.google.android.material.snackbar.Snackbar -import java.util.concurrent.TimeUnit -import javax.inject.Inject -import javax.inject.Provider import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.combine @@ -88,13 +85,15 @@ import kotlinx.coroutines.launch import logcat.logcat import nl.dionsegijn.konfetti.models.Shape import nl.dionsegijn.konfetti.models.Size +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Provider @InjectWith(ActivityScope::class) @ContributeToActivityStarter(AppTrackerActivityWithEmptyParams::class, screenName = "apptp.main") class DeviceShieldTrackerActivity : DuckDuckGoActivity(), DeviceShieldActivityFeedFragment.DeviceShieldActivityFeedListener { - @Inject lateinit var deviceShieldPixels: DeviceShieldPixels @@ -125,20 +124,22 @@ class DeviceShieldTrackerActivity : // we might get an update before options menu has been populated; temporarily cache value to use when menu populated private var vpnCachedState: VpnState? = null - private val feedConfig = DeviceShieldActivityFeedFragment.ActivityFeedConfig( - maxRows = MIN_ROWS_FOR_ALL_ACTIVITY, - timeWindow = 7, - timeWindowUnits = TimeUnit.DAYS, - showTimeWindowHeadings = false, - ) + private val feedConfig = + DeviceShieldActivityFeedFragment.ActivityFeedConfig( + maxRows = MIN_ROWS_FOR_ALL_ACTIVITY, + timeWindow = 7, + timeWindowUnits = TimeUnit.DAYS, + showTimeWindowHeadings = false, + ) private val viewModel: DeviceShieldTrackerActivityViewModel by bindViewModel() private lateinit var reportBreakage: ActivityResultLauncher - private val enableAppTPSwitchListener = CompoundButton.OnCheckedChangeListener { _, isChecked -> - viewModel.onAppTPToggleSwitched(isChecked) - } + private val enableAppTPSwitchListener = + CompoundButton.OnCheckedChangeListener { _, isChecked -> + viewModel.onAppTPToggleSwitched(isChecked) + } private val onInfoMessageClick = fun(action: DefaultAppTPMessageAction) { when (action) { @@ -153,11 +154,12 @@ class DeviceShieldTrackerActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - reportBreakage = registerForActivityResult(reportBreakageContract.get()) { - if (!it.isEmpty()) { - Snackbar.make(binding.root, R.string.atp_ReportBreakageSent, Snackbar.LENGTH_LONG).show() + reportBreakage = + registerForActivityResult(reportBreakageContract.get()) { + if (!it.isEmpty()) { + Snackbar.make(binding.root, R.string.atp_ReportBreakageSent, Snackbar.LENGTH_LONG).show() + } } - } setContentView(binding.root) setupToolbar(binding.includeToolbar.trackersToolbar) @@ -231,31 +233,31 @@ class DeviceShieldTrackerActivity : } private fun showDeviceShieldActivity() { - supportFragmentManager.beginTransaction() + supportFragmentManager + .beginTransaction() .replace( R.id.activity_list, DeviceShieldActivityFeedFragment.newInstance(feedConfig), - ) - .commitNow() + ).commitNow() } @OptIn(FlowPreview::class) private fun observeViewModel() { lifecycleScope.launch { - viewModel.getBlockedTrackersCount() + viewModel + .getBlockedTrackersCount() .combine(viewModel.getTrackingAppsCount()) { trackers, apps -> DeviceShieldTrackerActivityViewModel.TrackerCountInfo(trackers, apps) - } - .combine(viewModel.getRunningState()) { trackerCountInfo, runningState -> + }.combine(viewModel.getRunningState()) { trackerCountInfo, runningState -> DeviceShieldTrackerActivityViewModel.TrackerActivityViewState(trackerCountInfo, runningState) - } - .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) + }.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) .collect { renderViewState(it) } } lifecycleScope.launch { // This is a one-shot check as soon as the screen is shown - viewModel.getRunningState() + viewModel + .getRunningState() .map { it.alwaysOnState } .debounce(500) // give a bit of time so that pop doesn't just suddenly pops up .take(1) @@ -266,7 +268,8 @@ class DeviceShieldTrackerActivity : } } - viewModel.commands() + viewModel + .commands() .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) .onEach { processCommand(it) } .launchIn(lifecycleScope) @@ -348,8 +351,7 @@ class DeviceShieldTrackerActivity : } } }, - ) - .show() + ).show() } private fun launchRemoveFeatureConfirmationDialog() { @@ -370,8 +372,7 @@ class DeviceShieldTrackerActivity : deviceShieldPixels.didChooseToCancelRemoveTrakcingProtectionDialog() } }, - ) - .show() + ).show() } private fun showVpnConflictDialog() { @@ -392,8 +393,7 @@ class DeviceShieldTrackerActivity : onVpnConflictDialogDismiss() } }, - ) - .show() + ).show() } private fun showAlwaysOnConflictDialog() { @@ -413,42 +413,43 @@ class DeviceShieldTrackerActivity : onVpnConflictDialogDismiss() } }, - ) - .show() + ).show() } private fun launchAlwaysOnPromotionDialog() { val dialog = supportFragmentManager.findFragmentByTag(TAG_APPTP_PROMOTE_ALWAYS_ON_DIALOG) as? AlwaysOnAlertDialogFragment dialog?.dismiss() - AlwaysOnAlertDialogFragment.newAlwaysOnDialog( - object : AlwaysOnAlertDialogFragment.Listener { - override fun onGoToSettingsClicked() { - viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnOpenSettings) - } + AlwaysOnAlertDialogFragment + .newAlwaysOnDialog( + object : AlwaysOnAlertDialogFragment.Listener { + override fun onGoToSettingsClicked() { + viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnOpenSettings) + } - override fun onCanceled() { - viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnCancelled) - } - }, - ).show(supportFragmentManager, TAG_APPTP_PROMOTE_ALWAYS_ON_DIALOG) + override fun onCanceled() { + viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnCancelled) + } + }, + ).show(supportFragmentManager, TAG_APPTP_PROMOTE_ALWAYS_ON_DIALOG) } private fun launchAlwaysOnLockdownEnabledDialog() { val dialog = supportFragmentManager.findFragmentByTag(TAG_APPTP_PROMOTE_ALWAYS_ON_DIALOG) as? AlwaysOnAlertDialogFragment dialog?.dismiss() - AlwaysOnAlertDialogFragment.newAlwaysOnLockdownDialog( - object : AlwaysOnAlertDialogFragment.Listener { - override fun onGoToSettingsClicked() { - viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnOpenSettings) - } + AlwaysOnAlertDialogFragment + .newAlwaysOnLockdownDialog( + object : AlwaysOnAlertDialogFragment.Listener { + override fun onGoToSettingsClicked() { + viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnOpenSettings) + } - override fun onCanceled() { - viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnCancelled) - } - }, - ).show(supportFragmentManager, TAG_APPTP_PROMOTE_ALWAYS_ON_DIALOG) + override fun onCanceled() { + viewModel.onViewEvent(ViewEvent.PromoteAlwaysOnCancelled) + } + }, + ).show(supportFragmentManager, TAG_APPTP_PROMOTE_ALWAYS_ON_DIALOG) } fun onOpenAppProtection() { @@ -562,18 +563,20 @@ class DeviceShieldTrackerActivity : private suspend fun updateRunningState(runningState: VpnState) { if (!binding.deviceShieldTrackerNotifyMe.isVisible) { var newActivePlugin: ActivePlugin? = null - appTPStateMessagePluginPoint.getPlugins().firstNotNullOfOrNull { - it.getView(this, runningState, onInfoMessageClick)?.apply { - newActivePlugin = it - } - }?.let { - if (currenActivePlugin == null || newActivePlugin != currenActivePlugin) { - currenActivePlugin = newActivePlugin - binding.deviceShieldTrackerMessageContainer.show() - binding.deviceShieldTrackerMessageContainer.removeAllViews() - binding.deviceShieldTrackerMessageContainer.addView(it) - } - } ?: { + appTPStateMessagePluginPoint + .getPlugins() + .firstNotNullOfOrNull { + it.getView(this, runningState, onInfoMessageClick)?.apply { + newActivePlugin = it + } + }?.let { + if (currenActivePlugin == null || newActivePlugin != currenActivePlugin) { + currenActivePlugin = newActivePlugin + binding.deviceShieldTrackerMessageContainer.show() + binding.deviceShieldTrackerMessageContainer.removeAllViews() + binding.deviceShieldTrackerMessageContainer.addView(it) + } + } ?: { currenActivePlugin = null binding.deviceShieldTrackerMessageContainer.gone() } @@ -626,7 +629,10 @@ class DeviceShieldTrackerActivity : private sealed class VpnPermissionStatus { object Granted : VpnPermissionStatus() - data class Denied(val intent: Intent) : VpnPermissionStatus() + + data class Denied( + val intent: Intent, + ) : VpnPermissionStatus() } private fun showAppTpEnabledCta() { @@ -634,11 +640,12 @@ class DeviceShieldTrackerActivity : return } - val dialog = TypewriterDaxDialog.newInstance( - daxText = getString(R.string.atp_ActivityAppTpEnabledCtaText), - primaryButtonText = getString(R.string.atp_ActivityAppTpEnabledCtaButtonLabel), - hideButtonText = "", - ) + val dialog = + TypewriterDaxDialog.newInstance( + daxText = getString(R.string.atp_ActivityAppTpEnabledCtaText), + primaryButtonText = getString(R.string.atp_ActivityAppTpEnabledCtaButtonLabel), + hideButtonText = "", + ) dialog.setDaxDialogListener( object : DaxDialogListener { @@ -674,7 +681,8 @@ class DeviceShieldTrackerActivity : val displayWidth = resources.displayMetrics.widthPixels - binding.appTpEnabledKonfetti.build() + binding.appTpEnabledKonfetti + .build() .addColors(magenta, blue, purple, green, yellow) .setDirection(0.0, 359.0) .setSpeed(4f, 9f) @@ -699,10 +707,9 @@ class DeviceShieldTrackerActivity : internal fun intent( context: Context, onLaunchCallback: ResultReceiver? = null, - ): Intent { - return Intent(context, DeviceShieldTrackerActivity::class.java).apply { + ): Intent = + Intent(context, DeviceShieldTrackerActivity::class.java).apply { putExtra(RESULT_RECEIVER_EXTRA, onLaunchCallback) } - } } } diff --git a/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/PrivacyReportViewModelTest.kt b/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/PrivacyReportViewModelTest.kt index ad3ec798691e..461fb3d9910d 100644 --- a/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/PrivacyReportViewModelTest.kt +++ b/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/PrivacyReportViewModelTest.kt @@ -33,8 +33,6 @@ import com.duckduckgo.mobile.android.vpn.store.VpnDatabase import com.duckduckgo.mobile.android.vpn.trackers.AppTrackerEntity import com.duckduckgo.mobile.android.vpn.ui.onboarding.VpnStore import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel -import java.time.LocalDateTime -import kotlin.time.ExperimentalTime import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Assert.assertEquals @@ -43,11 +41,12 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.mock +import java.time.LocalDateTime +import kotlin.time.ExperimentalTime @ExperimentalTime @RunWith(AndroidJUnit4::class) class PrivacyReportViewModelTest { - @get:Rule @Suppress("unused") var instantTaskExecutorRule = InstantTaskExecutorRule() @@ -74,9 +73,11 @@ class PrivacyReportViewModelTest { } private fun prepareDb() { - db = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().targetContext, VpnDatabase::class.java) - .allowMainThreadQueries() - .build() + db = + Room + .inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().targetContext, VpnDatabase::class.java) + .allowMainThreadQueries() + .build() } @After @@ -85,39 +86,42 @@ class PrivacyReportViewModelTest { } @Test - fun whenTrackersBlockedThenReportHasTrackers() = runBlocking { - trackerFound("dax.com") - trackerFound("dax.com") - trackerFound("dax.com") - - testee.getReport().test { - val result = awaitItem() - assertEquals(3, result.trackers) - cancelAndConsumeRemainingEvents() + fun whenTrackersBlockedThenReportHasTrackers() = + runBlocking { + trackerFound("dax.com") + trackerFound("dax.com") + trackerFound("dax.com") + + testee.getReport().test { + val result = awaitItem() + assertEquals(3, result.trackers) + cancelAndConsumeRemainingEvents() + } } - } @Test - fun whenTrackersBlockedOutsideTimeWindowThenReturnEmpty() = runBlocking { - trackerFoundYesterday("dax.com") - trackerFoundYesterday("dax.com") - trackerFoundYesterday("dax.com") - - testee.getReport().test { - val result = awaitItem() - assertEquals(0, result.trackers) - cancelAndConsumeRemainingEvents() + fun whenTrackersBlockedOutsideTimeWindowThenReturnEmpty() = + runBlocking { + trackerFoundYesterday("dax.com") + trackerFoundYesterday("dax.com") + trackerFoundYesterday("dax.com") + + testee.getReport().test { + val result = awaitItem() + assertEquals(0, result.trackers) + cancelAndConsumeRemainingEvents() + } } - } @Test - fun whenNoTrackersBlockedThenReportIsEmpty() = runBlocking { - testee.getReport().test { - val result = awaitItem() - assertEquals(0, result.trackers) - cancelAndConsumeRemainingEvents() + fun whenNoTrackersBlockedThenReportIsEmpty() = + runBlocking { + testee.getReport().test { + val result = awaitItem() + assertEquals(0, result.trackers) + cancelAndConsumeRemainingEvents() + } } - } private fun trackerFoundYesterday(trackerDomain: String = "example.com") { trackerFound(trackerDomain, timestamp = yesterday()) @@ -129,14 +133,15 @@ class PrivacyReportViewModelTest { timestamp: String = DatabaseDateFormatter.bucketByHour(), ) { val defaultTrackingApp = TrackingApp("app.foo.com", "Foo App") - val tracker = VpnTracker( - trackerCompanyId = trackerCompanyId, - domain = domain, - timestamp = timestamp, - company = "", - companyDisplayName = "", - trackingApp = defaultTrackingApp, - ) + val tracker = + VpnTracker( + trackerCompanyId = trackerCompanyId, + domain = domain, + timestamp = timestamp, + company = "", + companyDisplayName = "", + trackingApp = defaultTrackingApp, + ) val trackers = listOf(tracker) repository.insert(trackers) db.vpnAppTrackerBlockingDao().insertTrackerEntities(trackers.map { it.asEntity() }) @@ -148,12 +153,11 @@ class PrivacyReportViewModelTest { return DatabaseDateFormatter.bucketByHour(yesterday) } - private fun VpnTracker.asEntity(): AppTrackerEntity { - return AppTrackerEntity( + private fun VpnTracker.asEntity(): AppTrackerEntity = + AppTrackerEntity( trackerCompanyId = this.trackerCompanyId, entityName = "name", score = 0, signals = emptyList(), ) - } } From 2b2e10ebd501f36297820be864b7e349c77655ef Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Mon, 21 Jul 2025 08:36:51 +0200 Subject: [PATCH 08/16] Update Spotless plugin version to 7.2.0 --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index 481a693132a8..db3d7a14d496 100644 --- a/versions.properties +++ b/versions.properties @@ -19,7 +19,7 @@ plugin.org.jetbrains.kotlin=1.9.24 plugin.com.google.devtools.ksp=1.9.24-1.0.20 -plugin.com.diffplug.spotless=7.1.0 +plugin.com.diffplug.spotless=7.2.0 version.android.billingclient=7.1.1 From 6167e0a7a5617c4d1c539863728fa1b93e81b8ff Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Tue, 22 Jul 2025 09:55:18 +0200 Subject: [PATCH 09/16] WIP --- code-formatting.gradle | 4 ++-- versions.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code-formatting.gradle b/code-formatting.gradle index f941107d6c15..8b761cd5d17d 100644 --- a/code-formatting.gradle +++ b/code-formatting.gradle @@ -3,8 +3,8 @@ apply plugin: 'com.diffplug.spotless' spotless { ratchetFrom 'origin/develop' kotlin { - target("src/**/*.kt") - ktlint("1.7.0") + target 'src/*/kotlin/**/*.kt', 'src/*/java/**/*.kt' + ktlint("1.7.1") trimTrailingWhitespace() leadingTabsToSpaces() } diff --git a/versions.properties b/versions.properties index db3d7a14d496..a49a7ca40fca 100644 --- a/versions.properties +++ b/versions.properties @@ -19,7 +19,7 @@ plugin.org.jetbrains.kotlin=1.9.24 plugin.com.google.devtools.ksp=1.9.24-1.0.20 -plugin.com.diffplug.spotless=7.2.0 +plugin.com.diffplug.spotless=7.2.1 version.android.billingclient=7.1.1 From 12b5434cf72e772d514cbe62b67a30405b55049b Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 23 Jul 2025 19:10:37 +0200 Subject: [PATCH 10/16] Refactor build.gradle to use plugins block for Kotlin and Java library I could remove this long ago when changing to plugin blocks, now it works --- lint-rules/build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lint-rules/build.gradle b/lint-rules/build.gradle index e9d01477ba18..eb5caeaa0ea0 100644 --- a/lint-rules/build.gradle +++ b/lint-rules/build.gradle @@ -14,8 +14,10 @@ * limitations under the License. */ -apply plugin: 'java-library' -apply plugin: 'kotlin' +plugins { + id 'java-library' + id 'kotlin' +} dependencies { compileOnly Kotlin.stdlib.jdk7 From 1d4bab8036f519309c876a3f9d1abf3eb1aee900 Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 23 Jul 2025 19:11:55 +0200 Subject: [PATCH 11/16] Remove unused Kotlin stdlib dependency from build.gradle This gets added by default, not need for us to specify it and avoids any issues. --- lint-rules/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/lint-rules/build.gradle b/lint-rules/build.gradle index eb5caeaa0ea0..a36c3fd610e5 100644 --- a/lint-rules/build.gradle +++ b/lint-rules/build.gradle @@ -20,7 +20,6 @@ plugins { } dependencies { - compileOnly Kotlin.stdlib.jdk7 compileOnly "com.android.tools.lint:lint-api:$lint_version" compileOnly "com.android.tools.lint:lint-checks:$lint_version" From 6d6b452cf05fbb0f984428aee59f8b3ddbf53d9d Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 23 Jul 2025 19:13:21 +0200 Subject: [PATCH 12/16] Update Kotlin to 2.2.0 & KSP to 2.2.0-2.0.2 Removed the extra version.kotlin declared as I believe it's redundant --- versions.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/versions.properties b/versions.properties index a49a7ca40fca..61649f34c8f9 100644 --- a/versions.properties +++ b/versions.properties @@ -15,9 +15,9 @@ plugin.org.jetbrains.dokka=1.8.20 plugin.com.osacky.fulladle=0.17.4 -plugin.org.jetbrains.kotlin=1.9.24 +plugin.org.jetbrains.kotlin=2.2.0 -plugin.com.google.devtools.ksp=1.9.24-1.0.20 +plugin.com.google.devtools.ksp=2.2.0-2.0.2 plugin.com.diffplug.spotless=7.2.1 @@ -111,7 +111,7 @@ version.io.jsonwebtoken..jjwt-orgjson=0.12.6 version.jakewharton.rxrelay2=2.0.0 -version.kotlin=1.9.24 +version.kotlin=2.2.0 version.kotlinx.coroutines=1.8.1 From e66b36b978f423ca1760073d7695085b5f5d3338 Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 23 Jul 2025 19:04:57 +0200 Subject: [PATCH 13/16] Fix broken Room code generation We use Kotlin CodeGen for Room in the app module, but not in other modules. This along with KSP2 breaks the update to Kotlin 2.2.0 We can turn KSP2 off for now and ensure that any module that adds the Room compiler generates Kotlin code. This will be the default in Kotlin 2.7.0+ but we cannot use it right now as we get occasional build issues. See the Asana task for more info. --- build.gradle | 16 ++++++++++++++++ gradle.properties | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f77c98963794..65b3b930d033 100644 --- a/build.gradle +++ b/build.gradle @@ -160,6 +160,22 @@ subprojects { } } } + + plugins.withId('com.google.devtools.ksp') { + afterEvaluate { + configurations.configureEach { config -> + if (config.name == 'ksp') { + config.dependencies.matching { + it.group == 'androidx.room' && it.name == 'room-compiler' + }.configureEach { + ksp { + arg("room.generateKotlin", "true") + } + } + } + } + } + } } task clean(type: Delete) { diff --git a/gradle.properties b/gradle.properties index 530c09b94b8b..c4e3bdf645c1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,4 @@ org.gradle.parallel=true org.gradle.configureondemand=true android.defaults.buildfeatures.buildconfig=true android.nonFinalResIds=false - +ksp.useKSP2=false From 4476655fe800d0d30a8c39bfd786a10cf8dc667d Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Wed, 23 Jul 2025 19:35:46 +0200 Subject: [PATCH 14/16] Update Anvil to 2.6.1, Dagger to 2.56.2 This also required some code changes to remove some deprecated classes. --- .../ContributeToActivityStarterCodeGenerator.kt | 6 +++--- .../ContributesPluginPointCodeGenerator.kt | 10 +++++----- .../ContributesRemoteFeatureCodeGenerator.kt | 10 +++++----- .../compiler/ContributesServiceApiCodeGenerator.kt | 6 +++--- .../ContributesSubComponentCodeGenerator.kt | 14 +++++++------- .../compiler/ContributesViewModelCodeGenerator.kt | 6 +++--- .../compiler/ContributesWorkerCodeGenerator.kt | 7 ++++--- versions.properties | 4 ++-- 8 files changed, 32 insertions(+), 31 deletions(-) diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributeToActivityStarterCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributeToActivityStarterCodeGenerator.kt index e0e83437e15a..b169a18cacdb 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributeToActivityStarterCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributeToActivityStarterCodeGenerator.kt @@ -40,7 +40,7 @@ class ContributeToActivityStarterCodeGenerator : CodeGenerator { override fun isApplicable(context: AnvilContext): Boolean = true - override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { + override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { return projectFiles.classAndInnerClassReferences(module) .toList() .filter { it.isAnnotatedWith(ContributeToActivityStarter::class.fqName) } @@ -52,7 +52,7 @@ class ContributeToActivityStarterCodeGenerator : CodeGenerator { .toList() } - private fun generateParameterToActivityMapper(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateParameterToActivityMapper(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val moduleClassName = "${vmClass.shortName}_ActivityMapper" @@ -73,7 +73,7 @@ class ContributeToActivityStarterCodeGenerator : CodeGenerator { } } - return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content, vmClass.containingFileAsJavaFile) } private fun createMapperClass( diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesPluginPointCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesPluginPointCodeGenerator.kt index fc6621ad4d69..1e637bf36afa 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesPluginPointCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesPluginPointCodeGenerator.kt @@ -45,7 +45,7 @@ class ContributesPluginPointCodeGenerator : CodeGenerator { override fun isApplicable(context: AnvilContext): Boolean = true - override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { + override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { return projectFiles.classAndInnerClassReferences(module) .toList() .filter { it.isAnnotatedWith(ContributesPluginPoint::class.fqName) } @@ -58,7 +58,7 @@ class ContributesPluginPointCodeGenerator : CodeGenerator { .toList() } - private fun generatePluginPoint(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generatePluginPoint(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val pluginPointClassName = "${vmClass.shortName}_PluginPoint" val scope = vmClass.annotations.first { it.fqName == ContributesPluginPoint::class.fqName }.scopeOrNull(0)!! @@ -132,10 +132,10 @@ class ContributesPluginPointCodeGenerator : CodeGenerator { ) } - return createGeneratedFile(codeGenDir, generatedPackage, pluginPointClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, pluginPointClassName, content, vmClass.containingFileAsJavaFile) } - private fun generateBindingModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateBindingModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val moduleClassName = "${vmClass.shortName}_PluginPoint_Module" val scope = vmClass.annotations.first { it.fqName == ContributesPluginPoint::class.fqName }.scopeOrNull(0)!! @@ -180,7 +180,7 @@ class ContributesPluginPointCodeGenerator : CodeGenerator { ).build() } - return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content, vmClass.containingFileAsJavaFile) } companion object { diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt index 4dbd919c7a5c..69a473af9201 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesRemoteFeatureCodeGenerator.kt @@ -51,7 +51,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection, - ): Collection { + ): Collection { return projectFiles.classAndInnerClassReferences(module) .toList() .filter { it.isAnnotatedWith(ContributesRemoteFeature::class.fqName) } @@ -72,7 +72,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { codeGenDir: File, module: ModuleDescriptor, customStorePresence: CustomStorePresence, - ): GeneratedFile { + ): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val generatedClassName = "${vmClass.shortName}_ProxyModule" val annotation = vmClass.annotations.first { it.fqName == ContributesRemoteFeature::class.fqName } @@ -187,7 +187,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { ) } - return createGeneratedFile(codeGenDir, generatedPackage, generatedClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, generatedClassName, content, vmClass.containingFileAsJavaFile) } private fun generateRemoteFeature( @@ -195,7 +195,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { codeGenDir: File, module: ModuleDescriptor, customStorePresence: CustomStorePresence, - ): GeneratedFile { + ): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val generatedClassName = "${vmClass.shortName}_RemoteFeature" val annotation = vmClass.annotations.first { it.fqName == ContributesRemoteFeature::class.fqName } @@ -319,7 +319,7 @@ class ContributesRemoteFeatureCodeGenerator : CodeGenerator { ) } - return createGeneratedFile(codeGenDir, generatedPackage, generatedClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, generatedClassName, content, vmClass.containingFileAsJavaFile) } private fun generateOptionalBindings( diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesServiceApiCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesServiceApiCodeGenerator.kt index 87b70f2229fe..cb217447c690 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesServiceApiCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesServiceApiCodeGenerator.kt @@ -46,7 +46,7 @@ class ContributesServiceApiCodeGenerator : CodeGenerator { override fun isApplicable(context: AnvilContext): Boolean = true - override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { + override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { return projectFiles.classAndInnerClassReferences(module) .toList() .filter { reference -> reference.isAnnotatedWith(serviceApiAnnotations.map { it.fqName }) } @@ -58,7 +58,7 @@ class ContributesServiceApiCodeGenerator : CodeGenerator { .toList() } - private fun generateServiceApiModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateServiceApiModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val moduleClassName = "${vmClass.shortName}_Module" @@ -125,7 +125,7 @@ class ContributesServiceApiCodeGenerator : CodeGenerator { ).build() } - return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content, vmClass.containingFileAsJavaFile) } private fun ClassReference.Psi.serviceApiClassName( diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesSubComponentCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesSubComponentCodeGenerator.kt index a24bc91eafd8..9c1bbba4acd8 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesSubComponentCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesSubComponentCodeGenerator.kt @@ -50,7 +50,7 @@ class ContributesSubComponentCodeGenerator : CodeGenerator { override fun isApplicable(context: AnvilContext): Boolean = true - override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { + override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { return projectFiles.classAndInnerClassReferences(module) .toList() .filter { it.isAnnotatedWith(InjectWith::class.fqName) } @@ -69,7 +69,7 @@ class ContributesSubComponentCodeGenerator : CodeGenerator { .toList() } - private fun generateActivityInjector(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateActivityInjector(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val activityInjectorInterfaceName = "${vmClass.shortName}_Injector" val scope = vmClass.annotations.first { it.fqName == InjectWith::class.fqName }.scopeOrNull(0)!! @@ -105,9 +105,9 @@ class ContributesSubComponentCodeGenerator : CodeGenerator { ).build() } - return createGeneratedFile(codeGenDir, generatedPackage, activityInjectorInterfaceName, content) + return createGeneratedFile(codeGenDir, generatedPackage, activityInjectorInterfaceName, content, vmClass.containingFileAsJavaFile) } - private fun generateSubcomponentFactory(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateSubcomponentFactory(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val subcomponentFactoryClassName = vmClass.subComponentName() val scope = vmClass.annotations.first { it.fqName == InjectWith::class.fqName }.scopeOrNull(0)!! @@ -159,7 +159,7 @@ class ContributesSubComponentCodeGenerator : CodeGenerator { ).build() } - return createGeneratedFile(codeGenDir, generatedPackage, subcomponentFactoryClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, subcomponentFactoryClassName, content, vmClass.containingFileAsJavaFile) } private fun generateParentComponentInterface(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): TypeSpec { @@ -184,7 +184,7 @@ class ContributesSubComponentCodeGenerator : CodeGenerator { .build() } - private fun generateSubcomponentFactoryBindingModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateSubcomponentFactoryBindingModule(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val moduleClassName = "${vmClass.subComponentName()}_Module" val scope = vmClass.annotations.first { it.fqName == InjectWith::class.fqName }.scopeOrNull(0)!! @@ -219,7 +219,7 @@ class ContributesSubComponentCodeGenerator : CodeGenerator { ).build() } - return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content, vmClass.containingFileAsJavaFile) } private fun FqName.subComponentAnnotation(module: ModuleDescriptor, delayGeneration: Boolean): AnnotationSpec { diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesViewModelCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesViewModelCodeGenerator.kt index 139c07eb6962..0635352c611f 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesViewModelCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesViewModelCodeGenerator.kt @@ -42,7 +42,7 @@ class ContributesViewModelCodeGenerator : CodeGenerator { override fun isApplicable(context: AnvilContext): Boolean = true - override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { + override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection): Collection { return projectFiles.classAndInnerClassReferences(module) .toList() .filter { it.isAnnotatedWith(ContributesViewModel::class.fqName) } @@ -52,7 +52,7 @@ class ContributesViewModelCodeGenerator : CodeGenerator { .toList() } - private fun generateFactoryPlugin(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile { + private fun generateFactoryPlugin(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val factoryClassName = "${vmClass.shortName}_ViewModelFactory" val scope = vmClass.annotations.first { it.fqName == ContributesViewModel::class.fqName }.scopeOrNull(0) @@ -122,7 +122,7 @@ class ContributesViewModelCodeGenerator : CodeGenerator { ).build() } - return createGeneratedFile(codeGenDir, generatedPackage, factoryClassName, content) + return createGeneratedFile(codeGenDir, generatedPackage, factoryClassName, content, vmClass.containingFileAsJavaFile) } companion object { diff --git a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesWorkerCodeGenerator.kt b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesWorkerCodeGenerator.kt index 2e256ffa22f5..a6cbfcc99fb6 100644 --- a/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesWorkerCodeGenerator.kt +++ b/anvil/anvil-compiler/src/main/java/com/duckduckgo/anvil/compiler/ContributesWorkerCodeGenerator.kt @@ -23,6 +23,7 @@ import com.squareup.anvil.annotations.ExperimentalAnvilApi import com.squareup.anvil.compiler.api.AnvilContext import com.squareup.anvil.compiler.api.CodeGenerator import com.squareup.anvil.compiler.api.GeneratedFile +import com.squareup.anvil.compiler.api.GeneratedFileWithSources import com.squareup.anvil.compiler.api.createGeneratedFile import com.squareup.anvil.compiler.internal.asClassName import com.squareup.anvil.compiler.internal.buildFile @@ -59,7 +60,7 @@ class ContributesWorkerCodeGenerator : CodeGenerator { codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection, - ): Collection = projectFiles.classAndInnerClassReferences(module) + ): Collection = projectFiles.classAndInnerClassReferences(module) .toList() .filter { it.isAnnotatedWith(ContributesWorker::class.fqName) } .flatMap { @@ -71,7 +72,7 @@ class ContributesWorkerCodeGenerator : CodeGenerator { vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor, - ): GeneratedFile { + ): GeneratedFileWithSources { val generatedPackage = vmClass.packageFqName.toString() val workerPluginName = "${vmClass.shortName}_WorkerInjectorPlugin" val scope = vmClass.annotations.first { it.fqName == ContributesWorker::class.fqName }.scopeOrNull(0) @@ -125,7 +126,7 @@ class ContributesWorkerCodeGenerator : CodeGenerator { ) } - return createGeneratedFile(codeGenDir, generatedPackage, workerPluginName, content) + return createGeneratedFile(codeGenDir, generatedPackage, workerPluginName, content, vmClass.containingFileAsJavaFile) } private fun generateCode( diff --git a/versions.properties b/versions.properties index 61649f34c8f9..057d9101522f 100644 --- a/versions.properties +++ b/versions.properties @@ -9,7 +9,7 @@ plugin.android=8.7.3 -plugin.com.squareup.anvil=2.5.0 +plugin.com.squareup.anvil=2.6.1 plugin.org.jetbrains.dokka=1.8.20 @@ -99,7 +99,7 @@ version.com.nhaarman.mockitokotlin2..mockito-kotlin=2.2.0 version.google.android.material=1.12.0 -version.google.dagger=2.51.1 +version.google.dagger=2.56.2 version.io.github.pcmind..leveldb=1.2 From 850aec3195dab9a6761f4a4dbc29ec8a75ebc693 Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Tue, 15 Jul 2025 17:18:24 +0100 Subject: [PATCH 15/16] Update Android Gradle Plugin version to 8.11.1 --- versions.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index 057d9101522f..6877b70792ea 100644 --- a/versions.properties +++ b/versions.properties @@ -7,7 +7,7 @@ #### suppress inspection "SpellCheckingInspection" for whole file #### suppress inspection "UnusedProperty" for whole file -plugin.android=8.7.3 +plugin.android=8.11.1 plugin.com.squareup.anvil=2.6.1 From eac94bd68d6251581b1b6ca9fd72b22dc1fd8eb8 Mon Sep 17 00:00:00 2001 From: Mike Scamell Date: Tue, 15 Jul 2025 17:33:48 +0100 Subject: [PATCH 16/16] Update Gradle wrapper to version 8.14.3 --- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 9 ++++----- gradlew.bat | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 34943 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

Gn4F+B~IivciU9^t0j-Mgao3 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 DDw4oP diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fb602ee2af06..78cb6e16a49f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6b11..23d15a936707 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -115,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -206,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9b42019c7915..5eed7ee84528 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell