10000 Enable Precise Location (#902) · chmodawk/Android@cd5e991 · GitHub
[go: up one dir, main page]

Skip to content

Commit cd5e991

Browse files
authored
Enable Precise Location (duckduckgo#902)
* hooking up permissions prompt * we check if the url is from DDG before requesting geo permissions * added custom dialog before we show the system permissions dialog * added data layer to retrieve, store and delete permissions * added data layer to retrieve, store and delete permissions * added appLocationPermission boolean to the settings data store * user can select to give location permission or deny it forever * we now react to the system permission and ask the user to save one * responging to dialog 2, need to use proper domain * cleaning up how we give permissions to the site * moving to proper dispatchers to store and fetch Room data * created custom layouts for both alert dialogs * properly fetching the data from the repository * cleaning up styling in permission dialogs * added new Settings screen that shows the sites that have enabled location * cleaning up favicon sizes and dialogs * properly showing the favicon in the list * we now also clear the geo location permission automatically based on the user settings * lint formatting * ensure we keep the location permission for a domain if it's fireproofed * remove permission if site is not fireproofed * if location request does not come from the current domain we deny it * added overflow menu to location permissions screen * added tests for LocationPermissionViewModel * added tests for GeoLocationPermissions * updating to the latest copy * cleaning up the geopermissions tests * cleaning up tests before starting * adding first test * added tests for browsertabviewmodel * cleaning up lint * adding latest translations, fixing the adapter so it actually shows properly * updating the logic to reflect the latest privacy team requirements * adding new tests for the new behavior * fix all tests for location permission * formatting fixes * if the user denies forever the system permission for a site, we don't show it again * more code cleanup * added tests for locations repository * ktlint formatting win * better test naming * update text from legal * we now show a message if the user visits a site that already has permanent location permission * adding tests for new message shown when then the domain visitted has location permission * added latest copy * fixed duplicated resources * updating text and dialog due to copy changes * fixing db migration merge issue * missing pixels and tests * removing unnecessary logs and fixing broken test * fix the issue where the location toggle wasn't clearing permissions * dialog should be cancellable is we are launching it from settings / location * properly saving deny always permission * check if the user denied system permission forever and don't show the custom dialog * cleaning up formatting * return early if the permission is granted * rollback to previous state * ensure we respect the user's decision of never asking again for system permission * added missing tests that were removed after merging * instead of passing the ViewModel that implements the dialog listener, we avoid that by making the fragment / activity implement the interface * fix issue where list of permissions wasn't refreshed on orientation change * fix issue in list * we should only avoid deleteing fireproofed location permissions when using the fire button * revert wrong removal of test * adding @ExperimentalCoroutinesApi to test class * addressing comments from the PR * ensure we also check for location sharing permissions before starting the whole flow * formatting * better naming for the function * more linting less fun * adding guard and data class to avoid null calls to location permission * checking for coarse location, it's possible that a device does not have gps but without network they won't be able to use the browser * making permissions check more readable * clean up tests and method to display website names
1 parent 6ea0767 commit cd5e991

File tree

52 files changed

+4044
-44
lines changed

Some content is hidden

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

52 files changed

+4044
-44
lines changed

app/schemas/com.duckduckgo.app.global.db.AppDatabase/25.json

Lines changed: 790 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 386 additions & 0 deletions
Large diffs are not rendered by default.

app/src/androidTest/java/com/duckduckgo/app/global/db/AppDatabaseTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ class AppDatabaseTest {
286286
createDatabaseAndMigrate(23, 24, migrationsProvider.MIGRATION_23_TO_24)
287287
}
288288

289+
@Test
290+
fun whenMigratingFromVersion24To25ThenValidationSucceeds() {
291+
createDatabaseAndMigrate(24, 25, migrationsProvider.MIGRATION_24_TO_25)
292+
}
293+
289294
private fun createDatabase(version: Int) {
290295
testHelper.createDatabase(TEST_DB_NAME, version).close()
291296
}

app/src/androidTest/java/com/duckduckgo/app/global/view/ClearPersonalDataActionTest.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.duckduckgo.app.browser.WebDataManager
2121
import com.duckduckgo.app.fire.AppCacheClearer
2222
import com.duckduckgo.app.fire.DuckDuckGoCookieManager
2323
import com.duckduckgo.app.fire.UnsentForgetAllPixelStore
24+
import com.duckduckgo.app.location.GeoLocationPermissions
2425
import com.duckduckgo.app.settings.db.SettingsDataStore
2526
import com.duckduckgo.app.tabs.model.TabRepository
2627
import com.nhaarman.mockitokotlin2.any
@@ -42,6 +43,7 @@ class ClearPersonalDataActionTest {
4243
private val mockSettingsDataStore: SettingsDataStore = mock()
4344
private val mockCookieManager: DuckDuckGoCookieManager = mock()
4445
private val mockAppCacheClearer: AppCacheClearer = mock()
46+
private val mockGeoLocationPermissions: GeoLocationPermissions = mock()
4547

4648
@Before
4749
fun setup() {
@@ -52,7 +54,8 @@ class ClearPersonalDataActionTest {
5254
mockTabRepository,
5355
mockSettingsDataStore,
5456
mockCookieManager,
55-
mockAppCacheClearer
57+
mockAppCacheClearer,
58+
mockGeoLocationPermissions
5659
)
5760
}
5861

@@ -91,4 +94,10 @@ class ClearPersonalDataActionTest {
9194
testee.clearTabsAndAllDataAsync(false, false)
9295
verify(mockTabRepository).deleteAll()
9396
}
97+
98+
@Test
99+
fun whenClearCalledThenGeoLocationPermissionsAreCleared() = runBlocking<Unit> {
100+
testee.clearTabsAndAllDataAsync(appInForeground = false, shouldFireDataClearPixel = false)
101+
verify(mockGeoLocationPermissions).clearAllButFireproofed()
102+
}
94103
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright (c) 2020 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.location
18+
19+
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
20+
import androidx.room.Room
21+
import androidx.test.platform.app.InstrumentationRegistry
22+
import com.duckduckgo.app.CoroutineTestRule
23+
import com.duckduckgo.app.InstantSchedulersRule
24+
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteDao
25+
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteEntity
26+
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteRepository
27+
import com.duckduckgo.app.global.db.AppDatabase
28+
import com.duckduckgo.app.location.data.LocationPermissionEntity
29+
import com.duckduckgo.app.location.data.LocationPermissionType
30+
import com.duckduckgo.app.location.data.LocationPermissionsDao
31+
import com.duckduckgo.app.location.data.LocationPermissionsRepository
32+
import com.duckduckgo.app.runBlocking
33+
import kotlinx.coroutines.ExperimentalCoroutinesApi
34+
import org.junit.After
35+
import org.junit.Assert.*
36+
import org.junit.Before
37+
import org.junit.Rule
38+
import org.junit.Test
39+
40+
@ExperimentalCoroutinesApi
41+
class GeoLocationPermissionsTest {
42+
43+
@get:Rule
44+
var instantTaskExecutorRule = InstantTaskExecutorRule()
45+
46+
@get:Rule
47+
val schedulers = InstantSchedulersRule()
48+
49+
@ExperimentalCoroutinesApi
50+
@get:Rule
51+
var coroutineRule = CoroutineTestRule()
52+
53+
private lateinit var locationPermissionsDao: LocationPermissionsDao
54+
55+
private lateinit var fireproofWebsiteDao: FireproofWebsiteDao
56+
57+
private lateinit var db: AppDatabase
58+
59+
private lateinit var geoLocationPermissions: GeoLocationPermissions
60+
61+
@Before
62+
fun before() {
63+
db = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().targetContext, AppDatabase::class.java)
64+
.allowMainThreadQueries()
65+
.build()
66+
locationPermissionsDao = db.locationPermissionsDao()
67+
fireproofWebsiteDao = db.fireproofWebsiteDao()
68+
69+
geoLocationPermissions = GeoLocationPermissionsManager(
70+
InstrumentationRegistry.getInstrumentation().targetContext,
71+
LocationPermissionsRepository(locationPermissionsDao, coroutineRule.testDispatcherProvider),
72+
FireproofWebsiteRepository(fireproofWebsiteDao, coroutineRule.testDispatcherProvider),
73+
coroutineRule.testDispatcherProvider
74+
)
75+
}
76+
77+
@After
78+
fun after() {
79+
db.close()
80+
}
81+
82+
@Test
83+
fun whenClearingAllPermissionsButFireproofedThenOnlyNonFireproofedSitesAreDeleted() = coroutineRule.runBlocking {
84+
givenFireproofWebsiteDomain("anotherdomain.com")
85+
givenLocationPermissionsDomain("https://domain.com")
86+
87+
val oldFireproofWebsites = fireproofWebsiteDao.fireproofWebsitesSync()
88+
assertEquals(oldFireproofWebsites.size, 1)
89+
90+
val oldLocationPermissions = locationPermissionsDao.allPermissions()
91+
assertEquals(oldLocationPermissions.size, 1)
92+
93+
geoLocationPermissions.clearAllButFireproofed()
94+
95+
val newFireproofWebsites = fireproofWebsiteDao.fireproofWebsitesSync()
96+
assertEquals(newFireproofWebsites.size, 1)
97+
98+
val newLocationPermissions = locationPermissionsDao.allPermissions()
99+
assertEquals(newLocationPermissions.size, 0)
100+
}
101+
102+
@Test
103+
fun whenClearingAllPermissionsButFireproofedAndNoFireproofedSitesThenAllSitePermissionsAreDeleted() = coroutineRule.runBlocking {
104+
givenLocationPermissionsDomain("https://domain.com")
105+
106+
val oldLocationPermissions = locationPermissionsDao.allPermissions()
107+
assertEquals(oldLocationPermissions.size, 1)
108+
109+
geoLocationPermissions.clearAllButFireproofed()
110+
111+
val newLocationPermissions = locationPermissionsDao.allPermissions()
112+
assertEquals(newLocationPermissions.size, 0)
113+
}
114+
115+
@Test
116+
fun whenClearingAllPermissionsButFireproofedAndSiteIsFireproofedThenNothingIsDeleted() = coroutineRule.runBlocking {
117+
givenFireproofWebsiteDomain("domain.com")
118+
givenLocationPermissionsDomain("https://domain.com")
119+
120+
val oldFireproofWebsites = fireproofWebsiteDao.fireproofWebsitesSync()
121+
assertEquals(oldFireproofWebsites.size, 1)
122+
123+
val oldLocationPermissions = locationPermissionsDao.allPermissions()
124+
assertEquals(oldLocationPermissions.size, 1)
125+
126+
geoLocationPermissions.clearAllButFireproofed()
127+
128+
val newFireproofWebsites = fireproofWebsiteDao.fireproofWebsitesSync()
129+
assertEquals(newFireproofWebsites.size, 1)
130+
131+
val newLocationPermissions = locationPermissionsDao.allPermissions()
132+
assertEquals(newLocationPermissions.size, 1)
133+
}
134+
135+
@Test
136+
fun whenClearingAllPermissionsThenAllPermissionsAreDeleted() = coroutineRule.runBlocking {
137+
givenLocationPermissionsDomain("https://domain.com")
138+
139+
val oldLocationPermissions = locationPermissionsDao.allPermissions()
140+
assertEquals(oldLocationPermissions.size, 1)
141+
142+
geoLocationPermissions.clearAll()
143+
144+
val newLocationPermissions = locationPermissionsDao.allPermissions()
145+
assertEquals(newLocationPermissions.size, 0)
146+
}
147+
148+
private fun givenFireproofWebsiteDomain(vararg fireproofWebsitesDomain: String) {
149+
fireproofWebsitesDomain.forEach {
150+
fireproofWebsiteDao.insert(FireproofWebsiteEntity(domain = it))
151+
}
152+
}
153+
154+
private fun givenLocationPermissionsDomain(domain: String, permissionType: LocationPermissionType = LocationPermissionType.ALLOW_ALWAYS) {
155+
locationPermissionsDao.insert(LocationPermissionEntity(domain = domain, permission = permissionType))
156+
}
157+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2020 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.location.data
18+
19+
import com.duckduckgo.app.global.view.asLocationPermissionOrigin
20+
import org.junit.Assert
21+
import org.junit.Test
22+
23+
class LocationPermissionEntityTest {
24+
25+
@Test
26+
fun whenDomainStartsWithHttpsThenDropPrefix() {
27+
val locationPermissionEntity = LocationPermissionEntity("https://www.example.com/", LocationPermissionType.ALLOW_ONCE)
28+
val host = locationPermissionEntity.forFireproofing()
BF61 29+
Assert.assertEquals("www.example.com", host)
30+
}
31+
32+
@Test
33+
fun whenDomainStartsWithHttpsUppercaseThenDropPrefix() {
34+
val locationPermissionEntity = LocationPermissionEntity("HTTPS://www.example.com/", LocationPermissionType.ALLOW_ONCE)
35+
val host = locationPermissionEntity.forFireproofing()
36+
Assert.assertEquals("www.example.com", host)
37+
}
38+
39+
@Test
40+
fun whenDomainDoesNotStartWithHttpsThenDomainUnchanged() {
41+
val locationPermissionEntity = LocationPermissionEntity("mobile.example.com/", LocationPermissionType.ALLOW_ONCE)
42+
val host = locationPermissionEntity.forFireproofing()
43+
Assert.assertEquals("mobile.example.com/", host)
44+
}
45+
46+
@Test
47+
fun whenDomainIsReturnedAsPermissionOriginThenDomainMatches() {
48+
val domain = "www.example.com"
49+
val host = domain.asLocationPermissionOrigin()
50+
Assert.assertEquals("https://www.example.com/", host)
51+
}
52+
53+
}

0 commit comments

Comments
 (0)
0