Skip to content

Commit 7ff1fc9

Browse files
authored
Add AppCacheExclusionPlugin so modules can exclude files from being removed (#6551)
Task/Issue URL: https://app.asana.com/1/137249556945/project/488551667048375/task/1210997578538480?focus=true ### Description Allow modules to exclude cache files from being removed. ### Steps to test this PR _No regression_ - [ ] Add the following to your logcat filter `message~:"Subscription features for base plan|Subscriptions response came from"` - [ ] Checkout this branch - [ ] Apply patch attached in https://app.asana.com/1/137249556945/project/488551667048375/task/1210997578538480?focus=true - [ ] Fresh install - [ ] Check in the logs you hit the Network and you receive the features - [ ] Fire button - [ ] Check in the logs you hit the Cache - [ ] Kill the app manually, open it again - [ ] Check in the logs you hit the Cache _fdroid build_ https://github.com/duckduckgo/Android/actions/runs/16828352038 should pass ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)|
1 parent 9f4be4f commit 7ff1fc9

File tree

5 files changed

+95
-6
lines changed

5 files changed

+95
-6
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.duckduckgo.app.fire.DataClearerForegroundAppRestartPixel
2828
import com.duckduckgo.app.fire.DataClearerTimeKeeper
2929
import com.duckduckgo.app.fire.UnsentForgetAllPixelStore
3030
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteRepository
31+
import com.duckduckgo.app.fire.model.AppCacheExclusionPlugin
3132
import com.duckduckgo.app.global.file.FileDeleter
3233
import com.duckduckgo.app.global.view.ClearDataAction
3334
import com.duckduckgo.app.global.view.ClearPersonalDataAction
@@ -43,6 +44,7 @@ import com.duckduckgo.app.trackerdetection.api.WebTrackersBlockedRepository
4344
import com.duckduckgo.app.trackerdetection.db.TdsDomainEntityDao
4445
import com.duckduckgo.app.trackerdetection.db.TdsEntityDao
4546
import com.duckduckgo.common.utils.DispatcherProvider
47+
import com.duckduckgo.common.utils.plugins.PluginPoint
4648
import com.duckduckgo.cookies.api.DuckDuckGoCookieManager
4749
import com.duckduckgo.di.scopes.AppScope
4850
import com.duckduckgo.history.api.NavigationHistory
@@ -125,8 +127,9 @@ object PrivacyModule {
125127
fun appCacheCleaner(
126128
context: Context,
127129
fileDeleter: FileDeleter,
130+
exclusionPlugins: PluginPoint<AppCacheExclusionPlugin>,
128131
): AppCacheClearer {
129-
return AndroidAppCacheClearer(context, fileDeleter)
132+
return AndroidAppCacheClearer(context, fileDeleter, exclusionPlugins)
130133
}
131134

132135
@Provides

app/src/main/java/com/duckduckgo/app/fire/AppCacheClearer.kt

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

1919
import android.content.Context
2020
import com.duckduckgo.app.browser.favicon.FileBasedFaviconPersister.Companion.FAVICON_PERSISTED_DIR
21+
import com.duckduckgo.app.fire.model.AppCacheExclusionPlugin
2122
import com.duckduckgo.app.global.api.NetworkApiCache
2223
import com.duckduckgo.app.global.file.FileDeleter
23-
import com.duckduckgo.subscriptions.impl.services.SubscriptionNetworkModule.Companion.SUBSCRIPTION_CACHE_FILE
24+
import com.duckduckgo.common.utils.plugins.PluginPoint
2425

2526
interface AppCacheClearer {
2627

@@ -30,10 +31,13 @@ interface AppCacheClearer {
3031
class AndroidAppCacheClearer(
3132
private val context: Context,
3233
private val fileDeleter: FileDeleter,
34+
private val exclusionPlugins: PluginPoint<AppCacheExclusionPlugin>,
3335
) : AppCacheClearer {
3436

3537
override suspend fun clearCache() {
36-
fileDeleter.deleteContents(context.cacheDir, FILENAMES_EXCLUDED_FROM_DELETION)
38+
val pluginExclusions = exclusionPlugins.getPlugins().flatMap { it.filenamesExcludedFromDeletion() }
39+
val exclusions = (FILENAMES_EXCLUDED_FROM_DELETION + pluginExclusions).distinct()
40+
fileDeleter.deleteContents(context.cacheDir, exclusions)
3741
}
3842

3943
companion object {
@@ -53,10 +57,7 @@ class AndroidAppCacheClearer(
5357
*/
5458
private const val NETWORK_CACHE_DIR = NetworkApiCache.FILE_NAME
5559

56-
// TODO: We need to allow other modules to contribute this list
57-
// https://app.asana.com/1/137249556945/project/1149059203486286/task/1210997578538480?focus=true
5860
private val FILENAMES_EXCLUDED_FROM_DELETION = listOf(
59-
SUBSCRIPTION_CACHE_FILE,
6061
WEBVIEW_CACHE_DIR,
6162
WEBVIEW_CACHE_DIR_LEGACY,
6263
NETWORK_CACHE_DIR,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025 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.fire
18+
19+
import com.duckduckgo.anvil.annotations.ContributesPluginPoint
20+
import com.duckduckgo.app.fire.model.AppCacheExclusionPlugin
21+
import com.duckduckgo.di.scopes.AppScope
22+
23+
@ContributesPluginPoint(
24+
scope = AppScope::class,
25+
boundType = AppCacheExclusionPlugin::class,
26+
)
27+
@Suppress("unused")
28+
interface AppCacheExclusionPluginPoint
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025 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.fire.model
18+
19+
/**
20+
* Modules can implement this plugin to contribute top-level filenames inside [Context.getCacheDir]
21+
* that MUST be preserved when the app clears its cache directory.
22+
*/
23+
interface AppCacheExclusionPlugin {
24+
/**
25+
* @return list of filenames (top-level entries in cacheDir) that should NOT be deleted
26+
*/
27+
fun filenamesExcludedFromDeletion(): List<String>
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2025 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.subscriptions.impl.services
18+
19+
import com.duckduckgo.app.fire.model.AppCacheExclusionPlugin
20+
import com.duckduckgo.di.scopes.AppScope
21+
import com.squareup.anvil.annotations.ContributesMultibinding
22+
import javax.inject.Inject
23+
24+
@ContributesMultibinding(AppScope::class)
25+
class SubscriptionsCacheExclusionPlugin @Inject constructor() : AppCacheExclusionPlugin {
26+
override fun filenamesExcludedFromDeletion(): List<String> {
27+
return listOf(SubscriptionNetworkModule.SUBSCRIPTION_CACHE_FILE)
28+
}
29+
}

0 commit comments

Comments
 (0)