Skip to content

Commit

Permalink
Navigation library (#3055)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/488551667048375/1204374977096192/f

### Description
Implement the navigation library, see [asana task](https://app.asana.com/0/488551667048375/1204374977096192/f) for more info

### Steps to test this PR
NA, nav library not used yet
  • Loading branch information
aitorvs authored Apr 14, 2023
1 parent 6a7e928 commit 8cecfce
Show file tree
Hide file tree
Showing 36 changed files with 615 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2023 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.anvil.annotations

import kotlin.reflect.KClass

/**
* Anvil annotation to generate and contribute the Map<ActivityParams, Class<ActivityParams>> to the activity starter
*
* Usage:
* ```kotlin
* @ContributeToActivityStarter(ExampleActivityParams::class)
* class MyActivity {
*
* }
*
* data class ExampleActivityParams(...) : ActivityParams
* ```
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
annotation class ContributeToActivityStarter(
/** The type of the input paramters received by the Activity */
val paramsType: KClass<*>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2023 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.anvil.compiler

import com.duckduckgo.anvil.annotations.ContributeToActivityStarter
import com.google.auto.service.AutoService
import com.squareup.anvil.annotations.ContributesMultibinding
import com.squareup.anvil.annotations.ExperimentalAnvilApi
import com.squareup.anvil.compiler.api.*
import com.squareup.anvil.compiler.internal.asClassName
import com.squareup.anvil.compiler.internal.buildFile
import com.squareup.anvil.compiler.internal.fqName
import com.squareup.anvil.compiler.internal.reference.*
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import java.io.File
import javax.inject.Inject
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile

/** This Anvil code generator allows generates a backend service API and its dagger bindings. */
@OptIn(ExperimentalAnvilApi::class)
@AutoService(CodeGenerator::class)
class ContributeToActivityStarterCodeGenerator : CodeGenerator {

override fun isApplicable(context: AnvilContext): Boolean = true

override fun generateCode(codeGenDir: File, module: ModuleDescriptor, projectFiles: Collection<KtFile>): Collection<GeneratedFile> {
return projectFiles.classAndInnerClassReferences(module)
.toList()
.filter { it.isAnnotatedWith(ContributeToActivityStarter::class.fqName) }
.flatMap {
listOf(
generateParameterToActivityMapper(it, codeGenDir, module),
)
}
.toList()
}

private fun generateParameterToActivityMapper(vmClass: ClassReference.Psi, codeGenDir: File, module: ModuleDescriptor): GeneratedFile {
val generatedPackage = vmClass.packageFqName.toString()
val moduleClassName = "${vmClass.shortName}_ActivityMapper"

if (!vmClass.directSuperTypeReferences().map { it.asClassReference() }.contains(duckduckgoActivityFqName.toClassReference(module))) {
throw AnvilCompilationException(
"${vmClass.fqName} must extend $duckduckgoActivityFqName",
element = vmClass.clazz.identifyingElement,
)
}

val mapperAnnotations = vmClass.annotations.filter { it.fqName == ContributeToActivityStarter::class.fqName }

val content = FileSpec.buildFile(generatedPackage, moduleClassName) {
for (annotation in mapperAnnotations) {
val paramsType = annotation.paramsTypeOrNull()!!
addType(createMapperClass(paramsType, vmClass, module)).build()
}
}

return createGeneratedFile(codeGenDir, generatedPackage, moduleClassName, content)
}

private fun createMapperClass(paramsType: ClassReference, vmClass: ClassReference.Psi, module: ModuleDescriptor): TypeSpec {
val moduleClassName = "${vmClass.shortName}_${paramsType.shortName}_Mapper"

return TypeSpec.classBuilder(moduleClassName)
.addAnnotation(
AnnotationSpec
.builder(ContributesMultibinding::class).addMember("scope = %T::class", appScopeFqName.asClassName(module))
.build(),
)
.addSuperinterface(paramToActivityMapperFqName.asClassName(module))
.addPrimaryConstructor()
.addFunction(
FunSpec.builder("map")
.addModifiers(KModifier.OVERRIDE)
.addParameter(
"activityParams",
activityParamsFqName.asClassName(module),
)
.returns(
Class::class.asClassName().parameterizedBy(
WildcardTypeName.producerOf(appCompatActivityFqName.asClassName(module)),
).copy(nullable = true),
)
.addCode(
"""
return if (activityParams is %T) {
%T::class.java
} else {
null
}
""".trimIndent(),
paramsType.asClassName(),
vmClass.asClassName(),
)
.build(),
)
.build()
}

private fun TypeSpec.Builder.addPrimaryConstructor(): TypeSpec.Builder {
val constructor = FunSpec.constructorBuilder()
.addAnnotation(AnnotationSpec.builder(Inject::class).build())
.build()

return this.primaryConstructor(constructor)
}

private fun AnnotationReference.paramsTypeOrNull(): ClassReference? {
return argumentAt("paramsType", 0)?.value()
}

companion object {
private val appScopeFqName = FqName("com.duckduckgo.di.scopes.AppScope")
private val paramToActivityMapperFqName = FqName("com.duckduckgo.navigation.api.GlobalActivityStarter.ParamToActivityMapper")
private val activityParamsFqName = FqName("com.duckduckgo.navigation.api.GlobalActivityStarter.ActivityParams")
private val appCompatActivityFqName = FqName("androidx.appcompat.app.AppCompatActivity")
private val duckduckgoActivityFqName = FqName("com.duckduckgo.app.global.DuckDuckGoActivity")
}
}
17 changes: 9 additions & 8 deletions app-tracking-protection/app-tracking-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@
*/

plugins {
id 'java-library'
id 'kotlin'
id 'com.android.library'
id 'kotlin-android'
}

apply from: "$rootProject.projectDir/code-formatting.gradle"

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
apply from: "$rootProject.projectDir/gradle/android-library.gradle"

dependencies {
implementation project(':navigation-api')

implementation Kotlin.stdlib.jdk7
}

android {
namespace 'com.duckduckgo.mobile.android.app.tracking'
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.mobile.android.app.tracking.ui

import com.duckduckgo.navigation.api.GlobalActivityStarter

/**
* Use this class to launch the AppTP Tracker Activity Screen
* ```kotlin
* globalActivityStarter.start(context, DeviceShieldTrackerActivityWithEmptyParams)
* ```
*/
object AppTrackerActivityWithEmptyParams : GlobalActivityStarter.ActivityParams

/**
* Use this class to launch the AppTP onboarding screen
* ```kotlin
* globalActivityStarter.start(context, AppTrackerOnboardingActivityWithEmptyParamsParams)
* ```
*/
object AppTrackerOnboardingActivityWithEmptyParamsParams : GlobalActivityStarter.ActivityParams
1 change: 1 addition & 0 deletions app-tracking-protection/vpn-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ android {

dependencies {
anvil project(path: ':anvil-compiler')
implementation project(path: ':navigation-api')
implementation project(path: ':anvil-annotations')
implementation project(path: ':vpn-store')
implementation project(path: ':vpn-api')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class ManageRecentAppsProtectionActivity :

companion object {
private const val REPORT_ISSUES_ANNOTATION = "report_issues_link"
fun intent(
internal fun intent(
context: Context,
): Intent {
return Intent(context, ManageRecentAppsProtectionActivity::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class TrackingProtectionExclusionListActivity :
UNPROTECTED_ONLY,
}

fun intent(
internal fun intent(
context: Context,
isRunning: Boolean = true,
filter: AppsFilter = AppsFilter.ALL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ class ReportBreakageAppListActivity : DuckDuckGoActivity(), ReportBreakageAppLis

companion object {
private const val USE_THIS_FORM_ANNOTATION = "use_this_form_link"
fun intent(context: Context): Intent {
internal fun intent(context: Context): Intent {
return Intent(context, ReportBreakageAppListActivity::class.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class ReportBreakageCategorySingleChoiceActivity : DuckDuckGoActivity() {

private const val APP_PACKAGE_ID_EXTRA = "APP_PACKAGE_ID_EXTRA"

fun intent(context: Context, brokenApp: BrokenApp): Intent {
internal fun intent(context: Context, brokenApp: BrokenApp): Intent {
return Intent(context, ReportBreakageCategorySingleChoiceActivity::class.java).apply {
putExtra(APP_PACKAGE_ID_EXTRA, brokenApp)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class DeviceShieldFAQActivity : DuckDuckGoActivity() {

companion object {

fun intent(context: Context): Intent {
internal fun intent(context: Context): Intent {
return Intent(context, DeviceShieldFAQActivity::class.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.duckduckgo.mobile.android.vpn.ui.onboarding

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.os.Build
Expand All @@ -27,10 +26,12 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2
import com.duckduckgo.anvil.annotations.ContributeToActivityStarter
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.app.global.DuckDuckGoActivity
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.mobile.android.app.tracking.ui.AppTrackerOnboardingActivityWithEmptyParamsParams
import com.duckduckgo.mobile.android.ui.view.dialog.TextAlertDialogBuilder
import com.duckduckgo.mobile.android.ui.view.getColorFromAttr
import com.duckduckgo.mobile.android.ui.viewbinding.viewBinding
Expand All @@ -50,6 +51,7 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@InjectWith(ActivityScope::class)
@ContributeToActivityStarter(AppTrackerOnboardingActivityWithEmptyParamsParams::class)
class VpnOnboardingActivity : DuckDuckGoActivity() {

@Inject
Expand Down Expand Up @@ -299,9 +301,5 @@ class VpnOnboardingActivity : DuckDuckGoActivity() {

companion object {
private const val REQUEST_ASK_VPN_PERMISSION = 101

fun intent(context: Context): Intent {
return Intent(context, VpnOnboardingActivity::class.java)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class DeviceShieldAppTrackersInfo : DuckDuckGoActivity() {

companion object {

fun intent(context: Context): Intent {
internal fun intent(context: Context): Intent {
return Intent(context, DeviceShieldAppTrackersInfo::class.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ class AppTPCompanyTrackersActivity : DuckDuckGoActivity() {
private const val EXTRA_APP_NAME = "EXTRA_APP_NAME"
private const val EXTRA_DATE = "EXTRA_DATE"

fun intent(
internal fun intent(
context: Context,
packageName: String,
appDisplayName: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class DeviceShieldMostRecentActivity : DuckDuckGoActivity() {
}

companion object {
fun intent(context: Context): Intent {
internal fun intent(context: Context): Intent {
return Intent(context, DeviceShieldMostRecentActivity::class.java)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.duckduckgo.anvil.annotations.ContributeToActivityStarter
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.app.global.DuckDuckGoActivity
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.mobile.android.app.tracking.ui.AppTrackerActivityWithEmptyParams
import com.duckduckgo.mobile.android.ui.view.DaxDialogListener
import com.duckduckgo.mobile.android.ui.view.InfoPanel.Companion.APPTP_SETTINGS_ANNOTATION
import com.duckduckgo.mobile.android.ui.view.InfoPanel.Companion.REPORT_ISSUES_ANNOTATION
Expand Down Expand Up @@ -79,6 +81,7 @@ import nl.dionsegijn.konfetti.models.Shape
import nl.dionsegijn.konfetti.models.Size

@InjectWith(ActivityScope::class)
@ContributeToActivityStarter(AppTrackerActivityWithEmptyParams::class)
class DeviceShieldTrackerActivity :
DuckDuckGoActivity(),
DeviceShieldActivityFeedFragment.DeviceShieldActivityFeedListener {
Expand Down Expand Up @@ -672,7 +675,7 @@ class DeviceShieldTrackerActivity :

private const val REQUEST_ASK_VPN_PERMISSION = 101

fun intent(
internal fun intent(
context: Context,
onLaunchCallback: ResultReceiver? = null,
): Intent {
Expand Down
Loading

0 comments on commit 8cecfce

Please sign in to comment.