Skip to content

Commit

Permalink
feature: support alternative dashboard layout (#3656)
Browse files Browse the repository at this point in the history
<!--
Note: This checklist is a reminder of our shared engineering
expectations.
The items in Bold are required
If your PR involves UI changes:
1. Upload screenshots or screencasts that illustrate the changes before
/ after
2. Add them under the UI changes section (feel free to add more columns
if needed)
If your PR does not involve UI changes, you can remove the **UI
changes** section

At a minimum, make sure your changes are tested in API 23 and one of the
more recent API levels available.
-->

Task/Issue URL: https://app.asana.com/0/0/1205648391652872/f

### Description

The updated Dashboard will have the following new interface, designed to
allow platforms to opt-in to additional UI features/layouts in a unified
way. For now just `primaryScreen.layout` is supported to enable an
experiment to increase adoption of the protections toggle

```ts
/**
 * Call this function to provide the dashboard with RemoteFeatureSettings
 */ 
declare function onChangeFeatureSettings(settings: RemoteFeatureSettings): void

/**
 * This describes the payload for feature settings
 */
export interface RemoteFeatureSettings {
  primaryScreen?: PrimaryScreen;
}

export interface PrimaryScreen {
  /**
   * A string to represent different screen layouts
   */
  layout: "default" | "highlighted-protections-toggle";
}
```

To provide different `layout` values, the PR includes support for a new
Privacy Configuration key, `privacyDashboard`

- Example PR adding the dashboard to privacy config the
duckduckgo/privacy-configuration#1349
- Example PR enabling the experiment
duckduckgo/privacy-configuration#1350

So, the experiment will be enabled remotely, and then the
`PrivacyDashboardViewModel` will use the values from the privacy config
to choose which layout the dashboard should show.

![toggle
exeriment](https://github.com/duckduckgo/Android/assets/1643522/b15ef4c6-00f1-4714-b6ed-6e562ee0d919)

## Pixel Params

This PR also adds the param `dashboard_highlighted_toggle`, to the
following pixels

- mp_wla (adding to the allowlist, pressing the toggle)
- mp_wlr (removing from the allowlist)
- m_bsr (feedback form is submitted)
- Privacy Triage: https://app.asana.com/0/0/1205648391652879/f


### Steps to test this PR

_Feature 1_
- [ ]
- [ ]

### UI changes
| Normal  | Experiment |
| ------ | ----- |

![Screenshot_20231004_094645](https://github.com/duckduckgo/Android/assets/1643522/7b6f56c3-f7d8-4f93-aa37-581499066848)|![Screenshot_20231004_094914](https://github.com/duckduckgo/Android/assets/1643522/6fdf2084-0c2d-4761-9b13-8e7b349b7df1)

---------

Co-authored-by: Shane Osbourne <[email protected]>
  • Loading branch information
shakyShane and Shane Osbourne authored Oct 13, 2023
1 parent 13b5b1e commit c12358b
Show file tree
Hide file tree
Showing 37 changed files with 31,304 additions and 55,042 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class BrokenSiteActivity : DuckDuckGoActivity() {
val consentManaged = intent.getBooleanExtra(CONSENT_MANAGED_EXTRA, false)
val consentOptOutFailed = intent.getBooleanExtra(CONSENT_OPT_OUT_FAILED_EXTRA, false)
val consentSelfTestFailed = intent.getBooleanExtra(CONSENT_SELF_TEST_FAILED_EXTRA, false)
val params = intent.getStringArrayExtra(BOOLEAN_PARAMS).orEmpty()
viewModel.setInitialBrokenSite(
url = url,
blockedTrackers = blockedTrackers,
Expand All @@ -78,6 +79,7 @@ class BrokenSiteActivity : DuckDuckGoActivity() {
consentManaged = consentManaged,
consentOptOutFailed = consentOptOutFailed,
consentSelfTestFailed = consentSelfTestFailed,
params = params,
)
}

Expand Down Expand Up @@ -165,6 +167,7 @@ class BrokenSiteActivity : DuckDuckGoActivity() {
private const val CONSENT_MANAGED_EXTRA = "CONSENT_MANAGED_EXTRA"
private const val CONSENT_OPT_OUT_FAILED_EXTRA = "CONSENT_OPT_OUT_FAILED_EXTRA"
private const val CONSENT_SELF_TEST_FAILED_EXTRA = "CONSENT_SELF_TEST_FAILED_EXTRA"
private const val BOOLEAN_PARAMS = "BOOLEAN_PARAMS"

fun intent(
context: Context,
Expand All @@ -179,6 +182,7 @@ class BrokenSiteActivity : DuckDuckGoActivity() {
intent.putExtra(CONSENT_MANAGED_EXTRA, data.consentManaged)
intent.putExtra(CONSENT_OPT_OUT_FAILED_EXTRA, data.consentOptOutFailed)
intent.putExtra(CONSENT_SELF_TEST_FAILED_EXTRA, data.consentSelfTestFailed)
intent.putExtra(BOOLEAN_PARAMS, data.params.toTypedArray())
return intent
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class BrokenSiteViewModel @Inject constructor(
private var consentManaged: Boolean = false
private var consentOptOutFailed: Boolean = false
private var consentSelfTestFailed: Boolean = false
private var params: Array<out String> = emptyArray()

var shuffledCategories = mutableListOf<BrokenSiteCategory>()

Expand All @@ -90,6 +91,7 @@ class BrokenSiteViewModel @Inject constructor(
consentManaged: Boolean,
consentOptOutFailed: Boolean,
consentSelfTestFailed: Boolean,
params: Array<out String>,
) {
this.url = url
this.blockedTrackers = blockedTrackers
Expand All @@ -99,6 +101,7 @@ class BrokenSiteViewModel @Inject constructor(
this.consentManaged = consentManaged
this.consentOptOutFailed = consentOptOutFailed
this.consentSelfTestFailed = consentSelfTestFailed
this.params = params
}

fun setCategories(categoryList: List<BrokenSiteCategory>): MutableList<BrokenSiteCategory> {
Expand Down Expand Up @@ -135,9 +138,19 @@ class BrokenSiteViewModel @Inject constructor(
}

brokenSiteSender.submitBrokenSiteFeedback(brokenSite)

val pixelParams = mutableMapOf(
Pixel.PixelParameter.URL to brokenSite.siteUrl,
)

// add any additional params - for example, from the privacy dashboard
params.forEach {
pixelParams[it] = true.toString()
}

pixel.fire(
AppPixelName.BROKEN_SITE_REPORTED,
mapOf(Pixel.PixelParameter.URL to brokenSite.siteUrl),
pixelParams,
)
}
command.value = Command.ConfirmAndFinish
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory()
testee.onSubmitPressed("webViewVersion", "description")
Expand Down Expand Up @@ -157,6 +158,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory()
testee.onSubmitPressed("webViewVersion", "description")
Expand Down Expand Up @@ -194,6 +196,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory()
testee.onSubmitPressed("webViewVersion", "description")
Expand Down Expand Up @@ -231,6 +234,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory()
testee.onSubmitPressed("webViewVersion", "description")
Expand All @@ -255,6 +259,33 @@ class BrokenSiteViewModelTest {
verify(mockCommandObserver).onChanged(Command.ConfirmAndFinish)
}

@Test
fun whenCanSubmitBrokenSiteAndUrlNotNullAndSubmitPressedThenReportAndPixelSubmittedWithParams() {
whenever(mockAmpLinks.lastAmpLinkInfo).thenReturn(AmpLinkInfo(trackingUrl, url))

testee.setInitialBrokenSite(
url = url,
blockedTrackers = "",
surrogates = "",
upgradedHttps = false,
urlParametersRemoved = false,
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = arrayOf("dashboard_highlighted_toggle"),
)
selectAndAcceptCategory()
testee.onSubmitPressed("webViewVersion", "description")

verify(mockPixel).fire(
AppPixelName.BROKEN_SITE_REPORTED,
mapOf(
"url" to trackingUrl,
"dashboard_highlighted_toggle" to true.toString(),
),
)
}

@Test
fun whenUrlIsDesktopThenSendDesktopParameter() {
testee.setInitialBrokenSite(
Expand All @@ -266,6 +297,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory()

Expand All @@ -285,6 +317,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory()

Expand All @@ -305,6 +338,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory(categoryIndex)

Expand All @@ -324,6 +358,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
selectAndAcceptCategory(0)
testee.onCategoryIndexChanged(1)
Expand All @@ -343,6 +378,7 @@ class BrokenSiteViewModelTest {
consentManaged = false,
consentOptOutFailed = false,
consentSelfTestFailed = false,
params = emptyArray(),
)
testee.onCategoryIndexChanged(1)
testee.onCategorySelectionCancelled()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ data class BrokenSiteData(
val consentManaged: Boolean,
val consentOptOutFailed: Boolean,
val consentSelfTestFailed: Boolean,
val params: List<String>,
) {
companion object {
fun fromSite(site: Site?): BrokenSiteData {
fun fromSite(site: Site?, params: List<String> = emptyList()): BrokenSiteData {
val events = site?.trackingEvents
val blockedTrackers = events?.filter { it.status == TrackerStatus.BLOCKED }
?.map { Uri.parse(it.trackerUrl).baseHost.orEmpty() }
Expand All @@ -59,6 +60,7 @@ data class BrokenSiteData(
consentManaged = consentManaged,
consentOptOutFailed = consentOptOutFailed,
consentSelfTestFailed = consentSelfTestFailed,
params = params,
)
}
}
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified node_modules/@duckduckgo/privacy-dashboard/build/app/img/icon_128.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified node_modules/@duckduckgo/privacy-dashboard/build/app/img/icon_48.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit c12358b

Please sign in to comment.