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/.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/.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/.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/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 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/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" /> 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/report/DeviceShieldAppTrackersInfo.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt similarity index 88% rename from app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldAppTrackersInfo.kt rename to app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt index 7825961728ae..c840a4f6bbfc 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldAppTrackersInfo.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldAppTrackersInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 DuckDuckGo + * Copyright (c) 2025 DuckDuckGo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.duckduckgo.mobile.android.vpn.ui.report +package com.duckduckgo.mobile.android.vpn.ui.privacyreport import android.content.Context import android.content.Intent @@ -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/report/DeviceShieldFragment.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt similarity index 71% rename from app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldFragment.kt rename to app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt index 4716f2b34341..fdcbe3ae605d 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/DeviceShieldFragment.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/DeviceShieldFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 DuckDuckGo + * Copyright (c) 2025 DuckDuckGo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.duckduckgo.mobile.android.vpn.ui.report +package com.duckduckgo.mobile.android.vpn.ui.privacyreport import android.app.ActivityOptions import android.os.Bundle @@ -38,15 +38,15 @@ import com.duckduckgo.mobile.android.vpn.R import com.duckduckgo.mobile.android.vpn.pixels.DeviceShieldPixels import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState.ENABLED 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.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 @@ -57,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() @@ -139,50 +138,57 @@ class DeviceShieldFragment : DuckDuckGoFragment() { deviceShieldCtaImageView.setImageResource(R.drawable.ic_apptp_warning) } - private fun renderTrackersBlockedWhenEnabled(trackerBlocked: PrivacyReportViewModel.PrivacyReportView.TrackersBlocked) { + private fun renderTrackersBlockedWhenEnabled(trackerBlocked: TrackersBlocked) { val trackersBlocked = trackerBlocked.trackers 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/report/PrivacyReportViewModel.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt similarity index 75% rename from app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/PrivacyReportViewModel.kt rename to app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt index d3fbb1d6cf68..654aa807d3e1 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/report/PrivacyReportViewModel.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/privacyreport/PrivacyReportViewModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 DuckDuckGo + * Copyright (c) 2025 DuckDuckGo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.duckduckgo.mobile.android.vpn.ui.report +package com.duckduckgo.mobile.android.vpn.ui.privacyreport import androidx.annotation.VisibleForTesting import androidx.lifecycle.ViewModel @@ -28,11 +28,13 @@ import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnState import com.duckduckgo.mobile.android.vpn.stats.AppTrackerBlockingStatsRepository import com.duckduckgo.mobile.android.vpn.ui.onboarding.VpnStore -import javax.inject.Inject +import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel.PrivacyReportView.TrackersBlocked +import com.duckduckgo.mobile.android.vpn.ui.privacyreport.PrivacyReportViewModel.PrivacyReportView.ViewState 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( @@ -42,35 +44,33 @@ class PrivacyReportViewModel @Inject constructor( vpnStateMonitor: VpnStateMonitor, private val dispatchers: DispatcherProvider, ) : ViewModel() { - - val viewStateFlow = vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN).combine(getReport()) { vpnState, trackersBlocked -> - PrivacyReportView.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()) { - 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 }) } } - } - 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( 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..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 @@ -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 @@ -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/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"> + 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/code-formatting.gradle b/code-formatting.gradle index 6e4fdbc1a6d8..8b761cd5d17d 100644 --- a/code-formatting.gradle +++ b/code-formatting.gradle @@ -1,26 +1,24 @@ apply plugin: 'com.diffplug.spotless' -apply plugin: 'org.jmailen.kotlinter' spotless { + ratchetFrom 'origin/develop' + kotlin { + target 'src/*/kotlin/**/*.kt', 'src/*/java/**/*.kt' + ktlint("1.7.1") + trimTrailingWhitespace() + leadingTabsToSpaces() + } java { target 'src/*/java/**/*.java' googleJavaFormat('1.22.0').aosp() targetExclude("**/FragmentStateAdapter.java") // temporary removeUnusedImports() trimTrailingWhitespace() - indentWithSpaces() + leadingTabsToSpaces() } format 'xml', { target '**/*.xml' - indentWithSpaces() + leadingTabsToSpaces() trimTrailingWhitespace() } } - -kotlinter { - disabledRules = ['no-wildcard-imports', 'filename', 'package-name', 'annotation-spacing'] -} - -tasks.register('code_format_checks') { - dependsOn 'spotlessCheck', 'lintKotlin' -} 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 diff --git a/lint-rules/build.gradle b/lint-rules/build.gradle index e9d01477ba18..a36c3fd610e5 100644 --- a/lint-rules/build.gradle +++ b/lint-rules/build.gradle @@ -14,11 +14,12 @@ * limitations under the License. */ -apply plugin: 'java-library' -apply plugin: 'kotlin' +plugins { + id 'java-library' + id 'kotlin' +} dependencies { - compileOnly Kotlin.stdlib.jdk7 compileOnly "com.android.tools.lint:lint-api:$lint_version" compileOnly "com.android.tools.lint:lint-checks:$lint_version" diff --git a/versions.properties b/versions.properties index ee2fb678c824..057d9101522f 100644 --- a/versions.properties +++ b/versions.properties @@ -9,19 +9,17 @@ 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 plugin.com.osacky.fulladle=0.17.4 -plugin.org.jetbrains.kotlin=1.9.24 +plugin.org.jetbrains.kotlin=2.2.0 -plugin.org.jmailen.kotlinter=3.12.0 +plugin.com.google.devtools.ksp=2.2.0-2.0.2 -plugin.com.google.devtools.ksp=1.9.24-1.0.20 - -plugin.com.diffplug.spotless=6.1.2 +plugin.com.diffplug.spotless=7.2.1 version.android.billingclient=7.1.1 @@ -101,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 @@ -113,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