Skip to content

Commit 9dadc8b

Browse files
Merge branch 'release/5.255.0'
2 parents d63ed19 + 25f843a commit 9dadc8b

File tree

240 files changed

+5753
-1074
lines changed

Some content is hidden

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

240 files changed

+5753
-1074
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Create Asana task for Privacy Review
2+
3+
on:
4+
pull_request:
5+
types: [ labeled ]
6+
7+
jobs:
8+
call-privacy-review:
9+
if: contains(github.event.label.name, 'privacy review')
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout native actions repo
13+
uses: actions/checkout@v4
14+
with:
15+
ref: v2.1
16+
# Do not change the below path (downstream actions expect it)
17+
path: native-github-asana-sync
18+
repository: duckduckgo/native-github-asana-sync
19+
20+
- name: Trigger review task
21+
uses: ./native-github-asana-sync/.github/actions/privacy-review/
22+
with:
23+
team_name: 'Android'
24+
asana_pat: ${{ secrets.ASANA_ACCESS_TOKEN }}
25+
github_pat: ${{ secrets.DAXMOBILE_ANDROID_AUTOMATION }}

app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/apps/TrackingProtectionAppsRepository.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo
2121
import android.content.pm.PackageManager
2222
import com.duckduckgo.common.utils.DispatcherProvider
2323
import com.duckduckgo.common.utils.extensions.isDdgApp
24+
import com.duckduckgo.common.utils.extensions.safeGetInstalledApplications
2425
import com.duckduckgo.di.scopes.AppScope
2526
import com.duckduckgo.mobile.android.vpn.apps.TrackingProtectionAppsRepository.ProtectionState
2627
import com.duckduckgo.mobile.android.vpn.apps.TrackingProtectionAppsRepository.ProtectionState.PROTECTED
@@ -113,15 +114,15 @@ class RealTrackingProtectionAppsRepository @Inject constructor(
113114

114115
private fun refreshInstalledApps() {
115116
logcat { "Excluded Apps: refreshInstalledApps" }
116-
installedApps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
117+
installedApps = packageManager.safeGetInstalledApplications(context)
117118
.asSequence()
118119
.filterNot { shouldNotBeShown(it) }
119120
}
120121

121122
override suspend fun getExclusionAppsList(): List<String> = withContext(dispatcherProvider.io()) {
122123
val exclusionList = appTrackerRepository.getAppExclusionList()
123124
val manualExclusionList = appTrackerRepository.getManualAppExclusionList()
124-
return@withContext packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
125+
return@withContext packageManager.safeGetInstalledApplications(context)
125126
.asSequence()
126127
.filter { isExcludedFromAppTP(it, exclusionList, manualExclusionList) }
127128
.sortedBy { it.name }

app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/apps/TrackingProtectionAppsRepositoryTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.duckduckgo.mobile.android.vpn.apps
1818

19+
import android.annotation.SuppressLint
1920
import android.content.pm.ApplicationInfo
2021
import android.content.pm.PackageManager
2122
import android.content.pm.PackageManager.NameNotFoundException
@@ -49,6 +50,7 @@ class TrackingProtectionAppsRepositoryTest {
4950
private lateinit var trackingProtectionAppsRepository: TrackingProtectionAppsRepository
5051

5152
@Before
53+
@SuppressLint("DenyListedApi")
5254
fun setup() {
5355
whenever(packageManager.getInstalledApplications(PackageManager.GET_META_DATA)).thenReturn(INSTALLED_APPS.asApplicationInfo())
5456
whenever(packageManager.getApplicationLabel(any())).thenReturn("App Name")

app-tracking-protection/vpn-internal/src/main/java/com/duckduckgo/vpn/internal/feature/VpnInternalSettingsActivity.kt

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

1919
import android.content.Intent
2020
import android.content.pm.ApplicationInfo
21-
import android.content.pm.PackageManager
2221
import android.os.Bundle
2322
import android.widget.CompoundButton
2423
import androidx.core.view.isVisible
@@ -35,6 +34,7 @@ import com.duckduckgo.common.ui.viewbinding.viewBinding
3534
import com.duckduckgo.common.utils.ConflatedJob
3635
import com.duckduckgo.common.utils.DispatcherProvider
3736
import com.duckduckgo.common.utils.extensions.isDdgApp
37+
import com.duckduckgo.common.utils.extensions.safeGetInstalledApplications
3838
import com.duckduckgo.di.scopes.ActivityScope
3939
import com.duckduckgo.mobile.android.app.tracking.AppTrackingProtection
4040
import com.duckduckgo.mobile.android.vpn.apps.isSystemApp
@@ -164,7 +164,7 @@ class VpnInternalSettingsActivity : DuckDuckGoActivity() {
164164
}
165165

166166
private fun refreshInstalledApps() {
167-
installedApps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
167+
installedApps = packageManager.safeGetInstalledApplications(this.applicationContext)
168168
.asSequence()
169169
.filterNot { shouldNotBeShown(it) }
170170
}

app-tracking-protection/vpn-internal/src/main/java/com/duckduckgo/vpn/internal/feature/rules/ExceptionRulesDebugActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.duckduckgo.common.ui.DuckDuckGoActivity
2929
import com.duckduckgo.common.ui.viewbinding.viewBinding
3030
import com.duckduckgo.common.utils.ConflatedJob
3131
import com.duckduckgo.common.utils.DispatcherProvider
32+
import com.duckduckgo.common.utils.extensions.safeGetInstalledApplications
3233
import com.duckduckgo.di.scopes.ActivityScope
3334
import com.duckduckgo.mobile.android.vpn.stats.AppTrackerBlockingStatsRepository
3435
import com.duckduckgo.mobile.android.vpn.store.VpnDatabase
@@ -113,7 +114,7 @@ class ExceptionRulesDebugActivity : DuckDuckGoActivity(), RuleTrackerView.RuleTr
113114
}
114115

115116
private fun getAppTrackers(): List<InstalledAppTrackers> {
116-
return packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
117+
return packageManager.safeGetInstalledApplications(this.applicationContext)
117118
.asSequence()
118119
.map { InstalledApp(it.packageName, packageManager.getApplicationLabel(it).toString()) }
119120
.map {

app/build.gradle

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import com.duckduckgo.gradle.ModuleCreator
2+
13
plugins {
24
id 'com.android.application'
35

@@ -608,6 +610,8 @@ tasks.register('assemblePlayReleaseDefaultVariant', Exec) {
608610
commandLine "$rootDir/gradlew", ':app:assemblePlayRelease', "-Pforce-default-variant"
609611
}
610612

611-
task newModule(type: com.duckduckgo.gradle.ModuleCreator) {
612-
feature = project.hasProperty('feature') ? project.getProperty('feature') : null
613+
tasks.register('newModule', ModuleCreator) {
614+
feature.set(providers.gradleProperty('feature'))
615+
repoRootDirectory.set(rootProject.layout.projectDirectory)
616+
appBuildGradleFile.set(layout.projectDirectory.file('build.gradle'))
613617
}

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

Lines changed: 3 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import androidx.lifecycle.LiveData
4141
import androidx.lifecycle.MutableLiveData
4242
import androidx.lifecycle.Observer
4343
import androidx.room.Room
44-
import androidx.test.annotation.UiThreadTest
4544
import androidx.test.filters.SdkSuppress
4645
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
4746
import com.duckduckgo.adclick.api.AdClickManager
@@ -222,8 +221,7 @@ import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggesti
222221
import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings
223222
import com.duckduckgo.browser.api.brokensite.BrokenSiteContext
224223
import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper
225-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM
226-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP
224+
import com.duckduckgo.browser.ui.omnibar.OmnibarType
227225
import com.duckduckgo.common.test.CoroutineTestRule
228226
import com.duckduckgo.common.test.InstantSchedulersRule
229227
import com.duckduckgo.common.ui.tabs.SwipingTabsFeature
@@ -617,10 +615,6 @@ class BrowserTabViewModelTest {
617615

618616
private val mockWebView: WebView = mock()
619617

620-
private val fakeAddDocumentStartJavaScriptPlugins = FakeAddDocumentStartJavaScriptPluginPoint()
621-
private val fakeMessagingPlugins = FakeWebMessagingPluginPoint()
622-
private val fakePostMessageWrapperPlugins = FakePostMessageWrapperPluginPoint()
623-
624618
private val mockDeviceAppLookup: DeviceAppLookup = mock()
625619

626620
@Before
@@ -675,7 +669,7 @@ class BrowserTabViewModelTest {
675669
whenever(mockSavedSitesRepository.getBookmarks()).thenReturn(bookmarksListFlow.consumeAsFlow())
676670
whenever(mockRemoteMessagingRepository.messageFlow()).thenReturn(remoteMessageFlow.consumeAsFlow())
677671
whenever(mockSettingsDataStore.automaticFireproofSetting).thenReturn(AutomaticFireproofSetting.ASK_EVERY_TIME)
678-
whenever(mockSettingsDataStore.omnibarPosition).thenReturn(TOP)
672+
whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_TOP)
679673
whenever(mockSettingsDataStore.isFullUrlEnabled).then { isFullSiteAddressEnabled }
680674
whenever(mockSSLCertificatesFeature.allowBypass()).thenReturn(mockEnabledToggle)
681675
whenever(subscriptions.shouldLaunchPrivacyProForUrl(any())).thenReturn(false)
@@ -850,9 +844,6 @@ class BrowserTabViewModelTest {
850844
externalIntentProcessingState = mockExternalIntentProcessingState,
851845
vpnMenuStateProvider = mockVpnMenuStateProvider,
852846
webViewCompatWrapper = mockWebViewCompatWrapper,
853-
addDocumentStartJavascriptPlugins = fakeAddDocumentStartJavaScriptPlugins,
854-
webMessagingPlugins = fakeMessagingPlugins,
855-
postMessageWrapperPlugins = fakePostMessageWrapperPlugins,
856847
addressBarTrackersAnimationFeatureToggle = mockAddressBarTrackersAnimationFeatureToggle,
857848
autoconsentPixelManager = mockAutoconsentPixelManager,
858849
)
@@ -5337,7 +5328,7 @@ class BrowserTabViewModelTest {
53375328
@Test
53385329
fun whenRefreshIsTriggeredByUserThenPrivacyProtectionsPopupManagerIsNotifiedWithBottomPosition() =
53395330
runTest {
5340-
whenever(mockSettingsDataStore.omnibarPosition).thenReturn(BOTTOM)
5331+
whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_BOTTOM)
53415332
testee.onRefreshRequested(triggeredByUser = false)
53425333
verify(mockPrivacyProtectionsPopupManager, never()).onPageRefreshTriggeredByUser(isOmnibarAtTheTop = false)
53435334
testee.onRefreshRequested(triggeredByUser = true)
@@ -7091,102 +7082,6 @@ class BrowserTabViewModelTest {
70917082
verify(mockOnboardingDesignExperimentManager).onWebPageFinishedLoading(url)
70927083
}
70937084

7094-
@Test
7095-
fun whenPageFinishedAndUpdateScriptOnPageFinishedEnabledThenAddDocumentStartJavaScript() =
7096-
runTest {
7097-
val url = "https://example.com"
7098-
val webViewNavState = WebViewNavigationState(mockStack, 100)
7099-
fakeAndroidConfigBrowserFeature.updateScriptOnPageFinished().setRawStoredState(State(true))
7100-
7101-
testee.pageFinished(mockWebView, webViewNavState, url)
7102-
7103-
assertEquals(1, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7104-
assertEquals(1, fakeAddDocumentStartJavaScriptPlugins.otherPlugin.countInitted)
7105-
}
7106-
7107-
@Test
7108-
fun whenPageFinishedAndUpdateScriptOnPageFinishedDisabledThenDoNotCallAddDocumentStartJavaScript() =
7109-
runTest {
7110-
val url = "https://example.com"
7111-
val webViewNavState = WebViewNavigationState(mockStack, 100)
7112-
fakeAndroidConfigBrowserFeature.updateScriptOnPageFinished().setRawStoredState(State(false))
7113-
7114-
testee.pageFinished(mockWebView, webViewNavState, url)
7115-
7116-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7117-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.otherPlugin.countInitted)
7118-
}
7119-
7120-
@Test
7121-
fun whenPrivacyProtectionsUpdatedAndUpdateScriptOnPageFinishedEnabledAndUpdateScriptOnProtectionsChangedEnabledThenAddDocumentStartJavaScript() =
7122-
runTest {
7123-
fakeAndroidConfigBrowserFeature.updateScriptOnPageFinished().setRawStoredState(State(true))
7124-
fakeAndroidConfigBrowserFeature.updateScriptOnProtectionsChanged().setRawStoredState(State(true))
7125-
7126-
testee.privacyProtectionsUpdated(mockWebView)
7127-
7128-
assertEquals(1, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7129-
assertEquals(1, fakeAddDocumentStartJavaScriptPlugins.otherPlugin.countInitted)
7130-
}
7131-
7132-
@Test
7133-
fun whenPrivacyProtectionsUpdatedAndUpdateScriptOnProtectionsChangedEnabledAndStopLoadingBeforeUpdatingScriptEnabledThenStopLoading() =
7134-
runTest {
7135-
fakeAndroidConfigBrowserFeature.updateScriptOnProtectionsChanged().setRawStoredState(State(true))
7136-
fakeAndroidConfigBrowserFeature.stopLoadingBeforeUpdatingScript().setRawStoredState(State(true))
7137-
7138-
testee.privacyProtectionsUpdated(mockWebView)
7139-
7140-
verify(mockWebView).stopLoading()
7141-
}
7142-
7143-
@Test
7144-
fun whenPrivacyProtectionsUpdatedAndUpdateScriptOnProtectionsChangedEnabledAndStopLoadingBeforeUpdatingScriptDisabledThenDoNotStopLoading() =
7145-
runTest {
7146-
fakeAndroidConfigBrowserFeature.updateScriptOnProtectionsChanged().setRawStoredState(State(true))
7147-
fakeAndroidConfigBrowserFeature.stopLoadingBeforeUpdatingScript().setRawStoredState(State(false))
7148-
7149-
testee.privacyProtectionsUpdated(mockWebView)
7150-
7151-
verify(mockWebView, never()).stopLoading()
7152-
}
7153-
7154-
@Test
7155-
fun whenPrivacyProtectionsUpdatedAndUpdateScriptOnPageFinishedTrueAndUpdateScriptOnProtectionsChangedFalseThenNotAddDocumentStartJavaScript() =
7156-
runTest {
7157-
fakeAndroidConfigBrowserFeature.updateScriptOnPageFinished().setRawStoredState(State(true))
7158-
fakeAndroidConfigBrowserFeature.updateScriptOnProtectionsChanged().setRawStoredState(State(false))
7159-
7160-
testee.privacyProtectionsUpdated(mockWebView)
7161-
7162-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7163-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.otherPlugin.countInitted)
7164-
}
7165-
7166-
@Test
7167-
fun whenPrivacyProtectionsUpdatedAndUpdateScriptOnPageFinishedDisabledThenAddDocumentStartJavaScriptOnlyOnCSS() =
7168-
runTest {
7169-
fakeAndroidConfigBrowserFeature.updateScriptOnPageFinished().setRawStoredState(State(false))
7170-
fakeAndroidConfigBrowserFeature.updateScriptOnProtectionsChanged().setRawStoredState(State(true))
7171-
7172-
testee.privacyProtectionsUpdated(mockWebView)
7173-
7174-
assertEquals(1, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7175-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.otherPlugin.countInitted)
7176-
}
7177-
7178-
@Test
7179-
fun whenPrivacyProtectionsUpdatedAndUpdateScriptOnPageFinishedFalseAndUpdateScriptOnProtectionsChangedFalseThenNotAddDocumentStartJavaScript() =
7180-
runTest {
7181-
fakeAndroidConfigBrowserFeature.updateScriptOnPageFinished().setRawStoredState(State(false))
7182-
fakeAndroidConfigBrowserFeature.updateScriptOnProtectionsChanged().setRawStoredState(State(false))
7183-
7184-
testee.privacyProtectionsUpdated(mockWebView)
7185-
7186-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7187-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.otherPlugin.countInitted)
7188-
}
7189-
71907085
@Test
71917086
fun whenCtaShownThenFireInContextDialogShownPixel() =
71927087
runTest {
@@ -7689,46 +7584,6 @@ class BrowserTabViewModelTest {
76897584
assertNull("SERP logo should be cleared when navigating to non-DuckDuckGo URL", omnibarViewState().serpLogo)
76907585
}
76917586

7692-
@UiThreadTest
7693-
@Test
7694-
fun whenConfigureWebViewThenCallAddDocumentStartJavaScript() =
7695-
runTest {
7696-
val mockCallback = mock<WebViewCompatMessageCallback>()
7697-
val webView = DuckDuckGoWebView(context)
7698-
assertEquals(0, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7699-
7700-
testee.configureWebView(webView, mockCallback)
7701-
7702-
assertEquals(1, fakeAddDocumentStartJavaScriptPlugins.cssPlugin.countInitted)
7703-
}
7704-
7705-
@UiThreadTest
7706-
@Test
7707-
fun whenConfigureWebViewThenCallAddMessageListener() =
7708-
runTest {
7709-
val mockCallback = mock<WebViewCompatMessageCallback>()
7710-
val webView = DuckDuckGoWebView(context)
7711-
assertFalse(fakeMessagingPlugins.plugin.registered)
7712-
7713-
testee.configureWebView(webView, mockCallback)
7714-
7715-
assertTrue(fakeMessagingPlugins.plugin.registered)
7716-
}
7717-
7718-
@UiThreadTest
7719-
@Test
7720-
fun whenPostMessageThenCallPostContentScopeMessage() =
7721-
runTest {
7722-
val data = SubscriptionEventData("feature", "method", JSONObject())
7723-
val webView = DuckDuckGoWebView(context)
7724-
7725-
assertFalse(fakePostMessageWrapperPlugins.plugin.postMessageCalled)
7726-
7727-
testee.postContentScopeMessage(data, webView)
7728-
7729-
assertTrue(fakePostMessageWrapperPlugins.plugin.postMessageCalled)
7730-
}
7731-
77327587
private fun aCredential(): LoginCredentials = LoginCredentials(domain = null, username = null, password = null)
77337588

77347589
private fun assertShowHistoryCommandSent(expectedStackSize: Int) {

app/src/androidTest/java/com/duckduckgo/app/fire/AutomaticDataClearerTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ class AutomaticDataClearerTest {
5454
private val mockWorkManager: WorkManager = mock()
5555
private val pixel: Pixel = mock()
5656
private val dataClearerForegroundAppRestartPixel =
57-
DataClearerForegroundAppRestartPixel(InstrumentationRegistry.getInstrumentation().targetContext, pixel)
57+
DataClearerForegroundAppRestartPixel(
58+
InstrumentationRegistry.getInstrumentation().targetContext,
59+
pixel,
60+
coroutineTestRule.testScope,
61+
coroutineTestRule.testDispatcherProvider,
62+
)
5863

5964
@UiThreadTest
6065
@Before

app/src/androidTest/java/com/duckduckgo/app/fire/DataClearerForegroundAppRestartPixelTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,21 @@ import com.duckduckgo.app.browser.BrowserActivity
2525
import com.duckduckgo.app.pixels.AppPixelName
2626
import com.duckduckgo.app.statistics.pixels.Pixel
2727
import com.duckduckgo.app.systemsearch.SystemSearchActivity
28+
import com.duckduckgo.common.test.CoroutineTestRule
2829
import org.junit.Before
30+
import org.junit.Rule
2931
import org.junit.Test
3032
import org.mockito.kotlin.mock
3133
import org.mockito.kotlin.verify
3234

3335
class DataClearerForegroundAppRestartPixelTest {
3436

37+
@get:Rule
38+
val coroutineTestRule: CoroutineTestRule = CoroutineTestRule()
39+
3540
private val context = InstrumentationRegistry.getInstrumentation().targetContext
3641
private val pixel = mock<Pixel>()
37-
private val testee = DataClearerForegroundAppRestartPixel(context, pixel)
42+
private val testee = DataClearerForegroundAppRestartPixel(context, pixel, coroutineTestRule.testScope, coroutineTestRule.testDispatcherProvider)
3843

3944
@Before
4045
fun setUp() {

0 commit comments

Comments
 (0)