Skip to content

Commit f139025

Browse files
authored
Add privacy terms
1 parent dafbb8a commit f139025

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1433
-20
lines changed

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserViewModelTest.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.duckduckgo.app.browser.BrowserViewModel.NavigationCommand
2323
import com.duckduckgo.app.browser.BrowserViewModel.ViewState
2424
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
2525
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
26+
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
2627
import com.nhaarman.mockito_kotlin.mock
2728
import org.junit.After
2829
import org.junit.Assert.*
@@ -42,6 +43,7 @@ class BrowserViewModelTest {
4243
private lateinit var viewStateObserver: Observer<ViewState>
4344
private lateinit var queryObserver: Observer<String>
4445
private lateinit var navigationObserver: Observer<NavigationCommand>
46+
private lateinit var termsOfServiceStore: TermsOfServiceStore
4547
private lateinit var testee: BrowserViewModel
4648

4749
private val testOmnibarConverter: OmnibarEntryConverter = object : OmnibarEntryConverter {
@@ -55,7 +57,8 @@ class BrowserViewModelTest {
5557
viewStateObserver = mock()
5658
queryObserver = mock()
5759
navigationObserver = mock()
58-
testee = BrowserViewModel(testOmnibarConverter, DuckDuckGoUrlDetector(), PrivacyMonitorRepository())
60+
termsOfServiceStore = mock()
61+
testee = BrowserViewModel(testOmnibarConverter, DuckDuckGoUrlDetector(), termsOfServiceStore, PrivacyMonitorRepository())
5962
testee.query.observeForever(queryObserver)
6063
testee.viewState.observeForever(viewStateObserver)
6164
testee.navigation.observeForever(navigationObserver)

app/src/androidTest/java/com/duckduckgo/app/privacymonitor/ui/PrivacyDashboardViewModelTest.kt

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import android.support.test.InstrumentationRegistry
2222
import com.duckduckgo.app.browser.R
2323
import com.duckduckgo.app.privacymonitor.HttpsStatus
2424
import com.duckduckgo.app.privacymonitor.PrivacyMonitor
25+
import com.duckduckgo.app.privacymonitor.model.TermsOfService
2526
import com.duckduckgo.app.privacymonitor.store.PrivacySettingsStore
2627
import com.nhaarman.mockito_kotlin.mock
2728
import com.nhaarman.mockito_kotlin.whenever
@@ -38,7 +39,6 @@ class PrivacyDashboardViewModelTest {
3839
var instantTaskExecutorRule = InstantTaskExecutorRule()
3940

4041
private lateinit var viewStateObserver: Observer<PrivacyDashboardViewModel.ViewState>
41-
private lateinit var monitor: PrivacyMonitor
4242
private lateinit var settingStore: PrivacySettingsStore
4343

4444
private val testee: PrivacyDashboardViewModel by lazy {
@@ -50,9 +50,7 @@ class PrivacyDashboardViewModelTest {
5050
@Before
5151
fun before() {
5252
viewStateObserver = mock()
53-
monitor = mock()
5453
settingStore = mock()
55-
whenever(monitor.https).thenReturn(HttpsStatus.SECURE)
5654
}
5755

5856
@After
@@ -91,6 +89,7 @@ class PrivacyDashboardViewModelTest {
9189

9290
@Test
9391
fun whenHttpsStatusIsSecureThenTextAndIconReflectSame() {
92+
val monitor = monitor()
9493
whenever(monitor.https).thenReturn(HttpsStatus.SECURE)
9594
testee.onPrivacyMonitorChanged(monitor)
9695
assertEquals(getStringResource(R.string.httpsGood), testee.viewState.value?.httpsText)
@@ -99,6 +98,7 @@ class PrivacyDashboardViewModelTest {
9998

10099
@Test
101100
fun whenHttpsStatusIsMixedThenTextAndIconReflectSame() {
101+
val monitor = monitor()
102102
whenever(monitor.https).thenReturn(HttpsStatus.MIXED)
103103
testee.onPrivacyMonitorChanged(monitor)
104104
assertEquals(getStringResource(R.string.httpsMixed), testee.viewState.value?.httpsText)
@@ -107,6 +107,7 @@ class PrivacyDashboardViewModelTest {
107107

108108
@Test
109109
fun whenHttpsStatusIsNoneThenTextAndIconReflectSame() {
110+
val monitor = monitor()
110111
whenever(monitor.https).thenReturn(HttpsStatus.NONE)
111112
testee.onPrivacyMonitorChanged(monitor)
112113
assertEquals(getStringResource(R.string.httpsBad), testee.viewState.value?.httpsText)
@@ -115,13 +116,15 @@ class PrivacyDashboardViewModelTest {
115116

116117
@Test
117118
fun whenNoTrackersNetworksThenNetworkIconIsGood() {
119+
val monitor = monitor()
118120
whenever(monitor.networkCount).thenReturn(0)
119121
testee.onPrivacyMonitorChanged(monitor)
120122
assertEquals(R.drawable.dashboard_networks_good, testee.viewState.value?.networksIcon)
121123
}
122124

123125
@Test
124126
fun whenTenTrackerNetworksAndAllBlockedThenNetworkIconIsGood() {
127+
val monitor = monitor()
125128
whenever(monitor.networkCount).thenReturn(10)
126129
whenever(monitor.allTrackersBlocked).thenReturn(true)
127130
testee.onPrivacyMonitorChanged(monitor)
@@ -130,6 +133,7 @@ class PrivacyDashboardViewModelTest {
130133

131134
@Test
132135
fun whenTenTrackerNetworksAndNotAllBlockedThenNetworkIconIsBad() {
136+
val monitor = monitor()
133137
whenever(monitor.networkCount).thenReturn(10)
134138
whenever(monitor.allTrackersBlocked).thenReturn(false)
135139
testee.onPrivacyMonitorChanged(monitor)
@@ -138,13 +142,15 @@ class PrivacyDashboardViewModelTest {
138142

139143
@Test
140144
fun whenNoMajorTrackersNetworksThenMajorNetworkIconIsGood() {
145+
val monitor = monitor()
141146
whenever(monitor.majorNetworkCount).thenReturn(0)
142147
testee.onPrivacyMonitorChanged(monitor)
143148
assertEquals(R.drawable.dashboard_major_networks_good, testee.viewState.value?.majorNetworksIcon)
144149
}
145150

146151
@Test
147152
fun whenTenMajorTrackerNetworksAndAllBlockedThenMajorNetworkIconIsGood() {
153+
val monitor = monitor()
148154
whenever(monitor.majorNetworkCount).thenReturn(10)
149155
whenever(monitor.allTrackersBlocked).thenReturn(true)
150156
testee.onPrivacyMonitorChanged(monitor)
@@ -153,6 +159,7 @@ class PrivacyDashboardViewModelTest {
153159

154160
@Test
155161
fun whenTenMajorTrackerNetworksAndNotAllBlockedThenMajorNetworkIconIsBad() {
162+
val monitor = monitor()
156163
whenever(monitor.majorNetworkCount).thenReturn(10)
157164
whenever(monitor.allTrackersBlocked).thenReturn(false)
158165
testee.onPrivacyMonitorChanged(monitor)
@@ -161,6 +168,7 @@ class PrivacyDashboardViewModelTest {
161168

162169
@Test
163170
fun whenNoTrackerNetworksThenNetworkTextShowsZeroBlocked() {
171+
val monitor = monitor()
164172
whenever(monitor.networkCount).thenReturn(0)
165173
whenever(monitor.allTrackersBlocked).thenReturn(true)
166174
testee.onPrivacyMonitorChanged(monitor)
@@ -169,6 +177,7 @@ class PrivacyDashboardViewModelTest {
169177

170178
@Test
171179
fun whenTenTrackerNetworksAndAllBlockedThenNetworkTextShowsTenBlocked() {
180+
val monitor = monitor()
172181
whenever(monitor.networkCount).thenReturn(10)
173182
whenever(monitor.allTrackersBlocked).thenReturn(true)
174183
testee.onPrivacyMonitorChanged(monitor)
@@ -177,6 +186,7 @@ class PrivacyDashboardViewModelTest {
177186

178187
@Test
179188
fun whenTenTrackersNetworksAndNotAllBlockedThenNetworkTextShowsTenFound() {
189+
val monitor = monitor()
180190
whenever(monitor.networkCount).thenReturn(10)
181191
whenever(monitor.allTrackersBlocked).thenReturn(false)
182192
testee.onPrivacyMonitorChanged(monitor)
@@ -185,6 +195,7 @@ class PrivacyDashboardViewModelTest {
185195

186196
@Test
187197
fun whenNoMajorTrackersNetworksThenMajorNetworkTextShowsZeroBlocked() {
198+
val monitor = monitor()
188199
whenever(monitor.majorNetworkCount).thenReturn(0)
189200
whenever(monitor.allTrackersBlocked).thenReturn(true)
190201
testee.onPrivacyMonitorChanged(monitor)
@@ -193,6 +204,7 @@ class PrivacyDashboardViewModelTest {
193204

194205
@Test
195206
fun whenTenMajorTrackerNetworksAndAllBlockedThenMajorNetworkTextShowsTenBlocked() {
207+
val monitor = monitor()
196208
whenever(monitor.majorNetworkCount).thenReturn(10)
197209
whenever(monitor.allTrackersBlocked).thenReturn(true)
198210
testee.onPrivacyMonitorChanged(monitor)
@@ -201,6 +213,7 @@ class PrivacyDashboardViewModelTest {
201213

202214
@Test
203215
fun whenTenMajorTrackerNetworksAndNotAllBlockedThenMajorNetworkTextShowsTenFound() {
216+
val monitor = monitor()
204217
whenever(monitor.majorNetworkCount).thenReturn(10)
205218
whenever(monitor.allTrackersBlocked).thenReturn(false)
206219
testee.onPrivacyMonitorChanged(monitor)
@@ -217,6 +230,44 @@ class PrivacyDashboardViewModelTest {
217230
assertEquals("0 Major Tracker Networks Blocked", testee.viewState.value?.majorNetworksText)
218231
}
219232

233+
@Test
234+
fun whenTermsAreGoodThenTextAndIconReflectSame() {
235+
val terms = TermsOfService(classification = "A", goodPrivacyTerms = listOf("good"))
236+
testee.onPrivacyMonitorChanged(monitor(terms))
237+
assertEquals(getStringResource(R.string.termsGood), testee.viewState.value?.termsText)
238+
assertEquals(R.drawable.dashboard_terms_good, testee.viewState.value?.termsIcon)
239+
}
240+
241+
@Test
242+
fun whenTermsArePoorThenTextAndIconReflectSame() {
243+
val terms = TermsOfService(classification = "E", badPrivacyTerms = listOf("bad"))
244+
testee.onPrivacyMonitorChanged(monitor(terms))
245+
assertEquals(getStringResource(R.string.termsBad), testee.viewState.value?.termsText)
246+
assertEquals(R.drawable.dashboard_terms_bad, testee.viewState.value?.termsIcon)
247+
}
248+
249+
@Test
250+
fun whenTermsAreMixedThenTextAndIconReflectSame() {
251+
val terms = TermsOfService(goodPrivacyTerms = listOf("good"), badPrivacyTerms = listOf("bad"))
252+
testee.onPrivacyMonitorChanged(monitor(terms))
253+
assertEquals(getStringResource(R.string.termsMixed), testee.viewState.value?.termsText)
254+
assertEquals(R.drawable.dashboard_terms_neutral, testee.viewState.value?.termsIcon)
255+
}
256+
257+
@Test
258+
fun whenTermsAreUnknownThenTextAndIconReflectSame() {
259+
testee.onPrivacyMonitorChanged(monitor())
260+
assertEquals(getStringResource(R.string.termsUnknown), testee.viewState.value?.termsText)
261+
assertEquals(R.drawable.dashboard_terms_neutral, testee.viewState.value?.termsIcon)
262+
}
263+
264+
private fun monitor(terms: TermsOfService = TermsOfService()): PrivacyMonitor {
265+
val monitor: PrivacyMonitor = mock()
266+
whenever(monitor.https).thenReturn(HttpsStatus.SECURE)
267+
whenever(monitor.termsOfService).thenReturn(terms)
268+
return monitor
269+
}
270+
220271
private fun getStringResource(id: Int): String =
221272
InstrumentationRegistry.getTargetContext().getString(id)
222273
}

app/src/main/java/com/duckduckgo/app/browser/BrowserViewModel.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ import android.arch.lifecycle.ViewModel
2121
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
2222
import com.duckduckgo.app.global.SingleLiveEvent
2323
import com.duckduckgo.app.privacymonitor.SiteMonitor
24+
import com.duckduckgo.app.privacymonitor.model.TermsOfService
2425
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
26+
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
2527
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
2628
import timber.log.Timber
2729

2830
class BrowserViewModel(
2931
private val queryUrlConverter: OmnibarEntryConverter,
3032
private val duckDuckGoUrlDetector: DuckDuckGoUrlDetector,
33+
private val termsOfServiceStore: TermsOfServiceStore,
3134
private val privacyMonitorRepository: PrivacyMonitorRepository) :
3235
WebViewClientListener, ViewModel() {
3336

@@ -106,7 +109,8 @@ class BrowserViewModel(
106109
}
107110
viewState.value = newViewState
108111
if (url != null) {
109-
siteMonitor = SiteMonitor(url)
112+
val terms = termsOfServiceStore.retrieveTerms(url) ?: TermsOfService()
113+
siteMonitor = SiteMonitor(url, terms)
110114
postSiteMonitor()
111115
}
112116
}

app/src/main/java/com/duckduckgo/app/di/NetworkModule.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ package com.duckduckgo.app.di
1818

1919
import android.content.Context
2020
import com.duckduckgo.app.browser.R
21-
import com.duckduckgo.app.trackerdetection.api.DisconnectJsonAdapter
21+
import com.duckduckgo.app.privacymonitor.api.TermsOfServiceListAdapter
22+
import com.duckduckgo.app.trackerdetection.api.DisconnectListJsonAdapter
2223
import com.duckduckgo.app.trackerdetection.api.TrackerListService
2324
import com.squareup.moshi.Moshi
2425
import dagger.Module
@@ -42,7 +43,10 @@ class NetworkModule {
4243

4344
@Provides
4445
@Singleton
45-
fun moshi(): Moshi = Moshi.Builder().add(DisconnectJsonAdapter()).build()
46+
fun moshi(): Moshi = Moshi.Builder()
47+
.add(DisconnectListJsonAdapter())
48+
.add(TermsOfServiceListAdapter())
49+
.build()
4650

4751
@Provides
4852
@Singleton

app/src/main/java/com/duckduckgo/app/di/PrivacyModule.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package com.duckduckgo.app.di
1818

1919
import com.duckduckgo.app.privacymonitor.store.PrivacySettingsSharedPreferences
2020
import com.duckduckgo.app.privacymonitor.store.PrivacySettingsStore
21+
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceRawStore
22+
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
2123
import dagger.Binds
2224
import dagger.Module
2325

@@ -27,4 +29,7 @@ abstract class PrivacyModule {
2729

2830
@Binds
2931
abstract fun bindPrivacySettingsStore(privacySettingsStore: PrivacySettingsSharedPreferences): PrivacySettingsStore
32+
33+
@Binds
34+
abstract fun bindTermsOfServiceStore(termsOfServiceStore: TermsOfServiceRawStore): TermsOfServiceStore
3035
}

app/src/main/java/com/duckduckgo/app/global/ViewModelFactory.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.duckduckgo.app.browser.omnibar.QueryUrlConverter
2525
import com.duckduckgo.app.privacymonitor.ui.PrivacyDashboardViewModel
2626
import com.duckduckgo.app.privacymonitor.store.PrivacyMonitorRepository
2727
import com.duckduckgo.app.privacymonitor.store.PrivacySettingsSharedPreferences
28+
import com.duckduckgo.app.privacymonitor.store.TermsOfServiceStore
2829
import javax.inject.Inject
2930

3031

@@ -34,13 +35,14 @@ class ViewModelFactory @Inject constructor(
3435
private val duckDuckGoUrlDetector: DuckDuckGoUrlDetector,
3536
private val privacyMonitorRepository: PrivacyMonitorRepository,
3637
private val privacySettingsStore: PrivacySettingsSharedPreferences,
38+
private val termsOfServiceStore: TermsOfServiceStore,
3739
private val context: Context
3840
) : ViewModelProvider.NewInstanceFactory() {
3941

4042
override fun <T : ViewModel> create(modelClass: Class<T>) =
4143
with(modelClass) {
4244
when {
43-
isAssignableFrom(BrowserViewModel::class.java) -> BrowserViewModel(queryUrlConverter, duckDuckGoUrlDetector, privacyMonitorRepository)
45+
isAssignableFrom(BrowserViewModel::class.java) -> BrowserViewModel(queryUrlConverter, duckDuckGoUrlDetector, termsOfServiceStore, privacyMonitorRepository)
4446
isAssignableFrom(PrivacyDashboardViewModel::class.java) -> PrivacyDashboardViewModel(context, privacySettingsStore)
4547
else ->
4648
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")

app/src/main/java/com/duckduckgo/app/privacymonitor/PrivacyMonitor.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
package com.duckduckgo.app.privacymonitor
1818

1919
import android.net.Uri
20+
import com.duckduckgo.app.privacymonitor.model.TermsOfService
2021
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
2122

2223
interface PrivacyMonitor {
2324

2425
val url: String
2526
val uri: Uri?
2627
val https: HttpsStatus
28+
val termsOfService: TermsOfService
2729
val trackerCount: Int
2830
val networkCount: Int
2931
val majorNetworkCount: Int

app/src/main/java/com/duckduckgo/app/privacymonitor/SiteMonitor.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ package com.duckduckgo.app.privacymonitor
1818

1919
import android.net.Uri
2020
import com.duckduckgo.app.global.isHttps
21+
import com.duckduckgo.app.privacymonitor.model.TermsOfService
2122
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
2223
import java.util.concurrent.CopyOnWriteArrayList
2324

24-
class SiteMonitor constructor(override val url: String) : PrivacyMonitor {
25+
class SiteMonitor(override val url: String, override val termsOfService: TermsOfService = TermsOfService()) : PrivacyMonitor {
2526

2627
override var hasHttpResources = false
2728

@@ -61,7 +62,7 @@ class SiteMonitor constructor(override val url: String) : PrivacyMonitor {
6162
.count()
6263

6364
override val allTrackersBlocked: Boolean
64-
get() = trackingEvents.none { it.blocked == false }
65+
get() = trackingEvents.none { !it.blocked }
6566

6667
override fun trackerDetected(event: TrackingEvent) {
6768
trackingEvents.add(event)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (c) 2017 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.app.privacymonitor.api
18+
19+
import com.squareup.moshi.Json
20+
21+
data class TermsOfServiceJson(val score: Int,
22+
@field:Json(name = "class")
23+
val classification: Any,
24+
val match: Match) {
25+
26+
data class Match(val good: List<String>, val bad: List<String>)
27+
}

0 commit comments

Comments
 (0)