Skip to content

Commit b0270b8

Browse files
committed
Merge branch 'release/5.18.2'
2 parents c8c0de6 + 22e14fa commit b0270b8

24 files changed

+561
-264
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) 2019 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.browser.rating.db
18+
19+
import androidx.room.Room
20+
import androidx.test.platform.app.InstrumentationRegistry
21+
import com.duckduckgo.app.global.db.AppDatabase
22+
import com.duckduckgo.app.global.rating.PromptCount
23+
import kotlinx.coroutines.runBlocking
24+
import org.junit.Assert.assertFalse
25+
import org.junit.Assert.assertTrue
26+
import org.junit.Before
27+
import org.junit.Test
28+
29+
@Suppress("RemoveExplicitTypeArguments")
30+
class AppEnjoymentDatabaseRepositoryTest {
31+
32+
private lateinit var testee: AppEnjoymentDatabaseRepository
33+
34+
private lateinit var database: AppDatabase
35+
private lateinit var dao: AppEnjoymentDao
36+
37+
38+
@Before
39+
fun setup() {
40+
database = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getInstrumentation().targetContext, AppDatabase::class.java)
41+
.allowMainThreadQueries()
42+
.build()
43+
44+
dao = database.appEnjoymentDao()
45+
testee = AppEnjoymentDatabaseRepository(dao)
46+
}
47+
48+
@Test
49+
fun whenFirstCreatedThenPrompt1CanBeShown() = runBlocking<Unit> {
50+
assertTrue(testee.canUserBeShownFirstPrompt())
51+
}
52+
53+
@Test
54+
fun whenUserGaveFeedbackForPrompt1ThenPrompt1CannotBeShown() = runBlocking<Unit> {
55+
testee.onUserSelectedToGiveFeedback(FIRST_PROMPT)
56+
assertFalse(testee.canUserBeShownFirstPrompt())
57+
}
58+
59+
@Test
60+
fun whenUserDeclinedToGiveFeedbackForPrompt1ThenPrompt1CannotBeShown() = runBlocking<Unit> {
61+
testee.onUserDeclinedToGiveFeedback(FIRST_PROMPT)
62+
assertFalse(testee.canUserBeShownFirstPrompt())
63+
}
64+
65+
@Test
66+
fun whenUserGaveRatingForPrompt1ThenPrompt1CannotBeShown() = runBlocking<Unit> {
67+
testee.onUserSelectedToRateApp(FIRST_PROMPT)
68+
assertFalse(testee.canUserBeShownFirstPrompt())
69+
}
70+
71+
@Test
72+
fun whenUserDeclinedRatingForPrompt1ThenPrompt1CannotBeShown() = runBlocking<Unit> {
73+
testee.onUserDeclinedToRateApp(FIRST_PROMPT)
74+
assertFalse(testee.canUserBeShownFirstPrompt())
75+
}
76+
77+
@Test
78+
fun whenUserDeclinedToSayWhetherEnjoyingForPrompt1ThenPrompt1CannotBeShown() = runBlocking<Unit> {
79+
testee.onUserDeclinedToSayIfEnjoyingApp(FIRST_PROMPT)
80+
assertFalse(testee.canUserBeShownFirstPrompt())
81+
}
82+
83+
@Test
84+
fun whenUserGaveFeedbackForPrompt2ThenPrompt2CannotBeShownAgain() = runBlocking<Unit> {
85+
testee.onUserSelectedToGiveFeedback(SECOND_PROMPT)
86+
assertFalse(testee.canUserBeShownSecondPrompt())
87+
}
88+
89+
@Test
90+
fun whenUserDeclinedToGiveFeedbackForPrompt2ThenPrompt2CannotBeShownAgain() = runBlocking<Unit> {
91+
testee.onUserDeclinedToGiveFeedback(SECOND_PROMPT)
92+
assertFalse(testee.canUserBeShownSecondPrompt())
93+
}
94+
95+
@Test
96+
fun whenUserGaveRatingForPrompt2ThenPrompt2CannotBeShownAgain() = runBlocking<Unit> {
97+
testee.onUserSelectedToRateApp(SECOND_PROMPT)
98+
assertFalse(testee.canUserBeShownSecondPrompt())
99+
}
100+
101+
@Test
102+
fun whenUserDeclinedRatingForPrompt2ThenPrompt2CannotBeShownAgain() = runBlocking<Unit> {
103+
testee.onUserDeclinedToRateApp(SECOND_PROMPT)
104+
assertFalse(testee.canUserBeShownSecondPrompt())
105+
}
106+
107+
@Test
108+
fun whenUserDeclinedToSayWhetherEnjoyingForPrompt2ThenPrompt2CannotBeShownAgain() = runBlocking<Unit> {
109+
testee.onUserDeclinedToSayIfEnjoyingApp(SECOND_PROMPT)
110+
assertFalse(testee.canUserBeShownSecondPrompt())
111+
}
112+
113+
companion object {
114+
private val FIRST_PROMPT = PromptCount(1)
115+
private val SECOND_PROMPT = PromptCount(2)
116+
}
117+
}

app/src/androidTest/java/com/duckduckgo/app/global/rating/InitialPromptDeciderTest.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import org.junit.Assert.assertTrue
2626
import org.junit.Before
2727
import org.junit.Test
2828

29-
@Suppress("RemoveExplicitTypeArguments")
29+
@Suppress("RemoveExplicitTypeArguments", "PrivatePropertyName")
3030
class InitialPromptDeciderTest {
3131

3232
private lateinit var testee: InitialPromptDecider
@@ -46,42 +46,42 @@ class InitialPromptDeciderTest {
4646
@Test
4747
fun whenUserHasNotSeenPromptBeforeAndNotUsedTheAppEnoughThenShouldNotSeePrompt() = runBlocking<Unit> {
4848
whenever(mockAppDaysUsedRepository.getNumberOfDaysAppUsed()).thenReturn(NOT_ENOUGH_DAYS)
49-
whenever(mockAppEnjoymentRepository.hasUserPreviouslySeenFirstPrompt()).thenReturn(false)
49+
whenever(mockAppEnjoymentRepository.canUserBeShownFirstPrompt()).thenReturn(true)
5050
assertFalse(testee.shouldShowPrompt())
5151
}
5252

5353
@Test
5454
fun whenUserHasNotSeenPromptBeforeAndUsedTheAppExactEnoughDaysThenShouldSeePrompt() = runBlocking<Unit> {
5555
whenever(mockAppDaysUsedRepository.getNumberOfDaysAppUsed()).thenReturn(EXACT_NUMBER_OF_DAYS)
56-
whenever(mockAppEnjoymentRepository.hasUserPreviouslySeenFirstPrompt()).thenReturn(false)
56+
whenever(mockAppEnjoymentRepository.canUserBeShownFirstPrompt()).thenReturn(true)
5757
assertTrue(testee.shouldShowPrompt())
5858
}
5959

6060
@Test
6161
fun whenUserHasNotSeenPromptBeforeAndUsedTheAppMoreThanEnoughDaysThenShouldSeePrompt() = runBlocking<Unit> {
6262
whenever(mockAppDaysUsedRepository.getNumberOfDaysAppUsed()).thenReturn(MORE_THAN_ENOUGH_DAYS)
63-
whenever(mockAppEnjoymentRepository.hasUserPreviouslySeenFirstPrompt()).thenReturn(false)
63+
whenever(mockAppEnjoymentRepository.canUserBeShownFirstPrompt()).thenReturn(true)
6464
assertTrue(testee.shouldShowPrompt())
6565
}
6666

6767
@Test
6868
fun whenUserHasSeenPromptBeforeAndNotUsedTheAppEnoughThenShouldNotSeePrompt() = runBlocking<Unit> {
6969
whenever(mockAppDaysUsedRepository.getNumberOfDaysAppUsed()).thenReturn(NOT_ENOUGH_DAYS)
70-
whenever(mockAppEnjoymentRepository.hasUserPreviouslySeenFirstPrompt()).thenReturn(true)
70+
whenever(mockAppEnjoymentRepository.canUserBeShownFirstPrompt()).thenReturn(false)
7171
assertFalse(testee.shouldShowPrompt())
7272
}
7373

7474
@Test
7575
fun whenUserHasSeenPromptBeforeAndUsedTheAppExactEnoughDaysThenShouldNotSeePrompt() = runBlocking<Unit> {
7676
whenever(mockAppDaysUsedRepository.getNumberOfDaysAppUsed()).thenReturn(EXACT_NUMBER_OF_DAYS)
77-
whenever(mockAppEnjoymentRepository.hasUserPreviouslySeenFirstPrompt()).thenReturn(true)
77+
whenever(mockAppEnjoymentRepository.canUserBeShownFirstPrompt()).thenReturn(false)
7878
assertFalse(testee.shouldShowPrompt())
7979
}
8080

8181
@Test
8282
fun whenUserHasSeenPromptBeforeAndUsedTheAppMoreThanEnoughDaysThenShouldNotSeePrompt() = runBlocking<Unit> {
8383
whenever(mockAppDaysUsedRepository.getNumberOfDaysAppUsed()).thenReturn(MORE_THAN_ENOUGH_DAYS)
84-
whenever(mockAppEnjoymentRepository.hasUserPreviouslySeenFirstPrompt()).thenReturn(true)
84+
whenever(mockAppEnjoymentRepository.canUserBeShownFirstPrompt()).thenReturn(false)
8585
assertFalse(testee.shouldShowPrompt())
8686
}
8787

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

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import com.duckduckgo.app.fire.DataClearer
3737
import com.duckduckgo.app.global.ApplicationClearDataState
3838
import com.duckduckgo.app.global.DuckDuckGoActivity
3939
import com.duckduckgo.app.global.intentText
40-
import com.duckduckgo.app.global.rating.PromptCount
4140
import com.duckduckgo.app.global.view.*
4241
import com.duckduckgo.app.playstore.PlayStoreUtils
4342
import com.duckduckgo.app.privacy.ui.PrivacyDashboardActivity
@@ -51,7 +50,7 @@ import org.jetbrains.anko.longToast
5150
import timber.log.Timber
5251
import javax.inject.Inject
5352

54-
class BrowserActivity : DuckDuckGoActivity(), AppEnjoymentDialogFragment.Listener, RateAppDialogFragment.Listener, GiveFeedbackDialogFragment.Listener {
53+
class BrowserActivity : DuckDuckGoActivity() {
5554

5655
@Inject
5756
lateinit var clearPersonalDataAction: ClearPersonalDataAction
@@ -227,9 +226,9 @@ class BrowserActivity : DuckDuckGoActivity(), AppEnjoymentDialogFragment.Listene
227226
is Refresh -> currentTab?.refresh()
228227
is Command.DisplayMessage -> applicationContext?.longToast(command.messageId)
229228
is Command.LaunchPlayStore -> launchPlayStore()
230-
is Command.ShowAppEnjoymentPrompt -> showAppEnjoymentPrompt(AppEnjoymentDialogFragment.create(command.promptCount))
231-
is Command.ShowAppRatingPrompt -> showAppEnjoymentPrompt(RateAppDialogFragment.create(command.promptCount))
232-
is Command.ShowAppFeedbackPrompt -> showAppEnjoymentPrompt(GiveFeedbackDialogFragment.create(command.promptCount))
229+
is Command.ShowAppEnjoymentPrompt -> showAppEnjoymentPrompt(AppEnjoymentDialogFragment.create(command.promptCount, viewModel))
230+
is Command.ShowAppRatingPrompt -> showAppEnjoymentPrompt(RateAppDialogFragment.create(command.promptCount, viewModel))
231+
is Command.ShowAppFeedbackPrompt -> showAppEnjoymentPrompt(GiveFeedbackDialogFragment.create(command.promptCount, viewModel))
233232
is Command.LaunchFeedbackView -> startActivity(FeedbackActivity.intent(this, brokenSite = false))
234233
}
235234
}
@@ -360,30 +359,6 @@ class BrowserActivity : DuckDuckGoActivity(), AppEnjoymentDialogFragment.Listene
360359
clearingInProgressView.show()
361360
}
362361

363-
override fun onUserSelectedAppIsEnjoyed(promptCount: PromptCount) {
364-
viewModel.onUserSelectedAppIsEnjoyed(promptCount)
365-
}
366-
367-
override fun onUserSelectedAppIsNotEnjoyed(promptCount: PromptCount) {
368-
viewModel.onUserSelectedAppIsNotEnjoyed(promptCount)
369-
}
370-
371-
override fun onUserSelectedToRateApp(promptCount: PromptCount) {
372-
viewModel.onUserSelectedToRateApp(promptCount)
373-
}
374-
375-
override fun onUserDeclinedToRateApp(promptCount: PromptCount) {
376-
viewModel.onUserDeclinedToRateApp(promptCount)
377-
}
378-
379-
override fun onUserSelectedToGiveFeedback(promptCount: PromptCount) {
380-
viewModel.onUserSelectedToGiveFeedback(promptCount)
381-
}
382-
383-
override fun onUserDeclinedToGiveFeedback(promptCount: PromptCount) {
384-
viewModel.onUserDeclinedToGiveFeedback(promptCount)
385-
}
386-
387362
private fun launchPlayStore() {
388363
playStoreUtils.launchPlayStore(this)
389364
}

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

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import androidx.lifecycle.ViewModel
2424
import com.duckduckgo.app.browser.BrowserViewModel.Command.DisplayMessage
2525
import com.duckduckgo.app.browser.BrowserViewModel.Command.Refresh
2626
import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
27+
import com.duckduckgo.app.browser.rating.ui.AppEnjoymentDialogFragment
28+
import com.duckduckgo.app.browser.rating.ui.GiveFeedbackDialogFragment
29+
import com.duckduckgo.app.browser.rating.ui.RateAppDialogFragment
2730
import com.duckduckgo.app.fire.DataClearer
2831
import com.duckduckgo.app.global.ApplicationClearDataState
2932
import com.duckduckgo.app.global.SingleLiveEvent
@@ -46,7 +49,11 @@ class BrowserViewModel(
4649
private val dataClearer: DataClearer,
4750
private val appEnjoymentPromptEmitter: AppEnjoymentPromptEmitter,
4851
private val appEnjoymentUserEventRecorder: AppEnjoymentUserEventRecorder
49-
) : ViewModel(), CoroutineScope {
52+
) : AppEnjoymentDialogFragment.Listener,
53+
RateAppDialogFragment.Listener,
54+
GiveFeedbackDialogFragment.Listener,
55+
ViewModel(),
56+
CoroutineScope {
5057

5158
override val coroutineContext: CoroutineContext
5259
get() = Dispatchers.Main
@@ -152,31 +159,44 @@ class BrowserViewModel(
152159
appEnjoymentPromptEmitter.promptType.removeObserver(appEnjoymentObserver)
153160
}
154161

155-
fun onUserSelectedAppIsEnjoyed(promptCount: PromptCount) {
162+
override fun onUserSelectedAppIsEnjoyed(promptCount: PromptCount) {
156163
appEnjoymentUserEventRecorder.onUserEnjoyingApp(promptCount)
157164
}
158165

159-
fun onUserSelectedAppIsNotEnjoyed(promptCount: PromptCount) {
166+
override fun onUserSelectedAppIsNotEnjoyed(promptCount: PromptCount) {
160167
appEnjoymentUserEventRecorder.onUserNotEnjoyingApp(promptCount)
161168
}
162169

163-
fun onUserSelectedToRateApp(promptCount: PromptCount) {
170+
override fun onUserSelectedToRateApp(promptCount: PromptCount) {
164171
command.value = Command.LaunchPlayStore
165172

166173
launch { appEnjoymentUserEventRecorder.onUserSelectedToRateApp(promptCount) }
167174
}
168175

169-
fun onUserDeclinedToRateApp(promptCount: PromptCount) {
176+
override fun onUserDeclinedToRateApp(promptCount: PromptCount) {
170177
launch { appEnjoymentUserEventRecorder.userDeclinedToRateApp(promptCount) }
171178
}
172179

173-
fun onUserSelectedToGiveFeedback(promptCount: PromptCount) {
180+
override fun onUserSelectedToGiveFeedback(promptCount: PromptCount) {
174181
command.value = Command.LaunchFeedbackView
175182

176183
launch { appEnjoymentUserEventRecorder.onUserSelectedToGiveFeedback(promptCount) }
177184
}
178185

179-
fun onUserDeclinedToGiveFeedback(promptCount: PromptCount) {
186+
override fun onUserDeclinedToGiveFeedback(promptCount: PromptCount) {
180187
launch { appEnjoymentUserEventRecorder.onUserDeclinedToGiveFeedback(promptCount) }
181188
}
189+
190+
override fun onUserCancelledAppEnjoymentDialog(promptCount: PromptCount) {
191+
launch { appEnjoymentUserEventRecorder.onUserDeclinedToSayIfEnjoyingApp(promptCount)}
192+
}
193+
194+
override fun onUserCancelledRateAppDialog(promptCount: PromptCount) {
195+
onUserDeclinedToRateApp(promptCount)
196+
}
197+
198+
override fun onUserCancelledGiveFeedbackDialog(promptCount: PromptCount) {
199+
onUserDeclinedToGiveFeedback(promptCount)
200+
}
201+
182202
}

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

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,12 @@ package com.duckduckgo.app.browser
1818

1919
import android.content.Context
2020
import android.os.Build
21-
import android.util.Log
2221
import android.webkit.WebStorage
2322
import android.webkit.WebView
2423
import android.webkit.WebViewDatabase
2524
import com.duckduckgo.app.browser.session.WebViewSessionStorage
2625
import com.duckduckgo.app.fire.DuckDuckGoCookieManager
2726
import com.duckduckgo.app.global.performance.measureExecution
28-
import java.io.File
2927
import javax.inject.Inject
3028

3129
interface WebDataManager {
@@ -48,8 +46,6 @@ class WebViewDataManager @Inject constructor(
4846
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
4947
clearFormData(WebViewDatabase.getInstance(context))
5048
}
51-
52-
deleteWebViewDirectory(context)
5349
}
5450

5551
private fun clearCache(webView: WebView) {
@@ -86,19 +82,6 @@ class WebViewDataManager @Inject constructor(
8682
}
8783
}
8884

89-
private fun deleteWebViewDirectory(context: Context) {
90-
val webViewDirectory = File(context.applicationInfo.dataDir, WEBVIEW_STORAGE_DIRECTORY)
91-
measureExecution("Deleted WebView directory ${webViewDirectory.name}") {
92-
webViewDirectory.listFiles()?.forEach { deleteFile(it) }
93-
}
94-
}
95-
96-
private fun deleteFile(it: File) {
97-
measureExecution("Deleted file: ${it.name}", Log.VERBOSE) {
98-
it.deleteRecursively()
99-
}
100-
}
101-
10285
override suspend fun clearExternalCookies() {
10386
measureExecution("Cleared cookies") {
10487
cookieManager.removeExternalCookies()
@@ -110,8 +93,4 @@ class WebViewDataManager @Inject constructor(
11093
webViewSessionStorage.deleteAllSessions()
11194
}
11295
}
113-
114-
companion object {
115-
private const val WEBVIEW_STORAGE_DIRECTORY = "app_webview"
116-
}
11796
}

app/src/main/java/com/duckduckgo/app/browser/rating/db/AppEnjoyment.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ private const val TYPE_PROVIDED_RATING = 1
2323
private const val TYPE_DECLINED_RATING = 2
2424
private const val TYPE_PROVIDED_FEEDBACK = 3
2525
private const val TYPE_DECLINED_FEEDBACK = 4
26+
private const val TYPE_DECLINED_TO_PARTICIPATE = 5
2627

2728
@Dao
2829
interface AppEnjoymentDao {
@@ -42,7 +43,10 @@ interface AppEnjoymentDao {
4243
@Query("SELECT * from app_enjoyment WHERE eventType = $TYPE_DECLINED_FEEDBACK AND promptCount = :promptCount")
4344
fun hasUserDeclinedFeedback(promptCount: Int): Boolean
4445

45-
@Query("SELECT timestamp FROM app_enjoyment WHERE eventType=$TYPE_DECLINED_RATING OR eventType=$TYPE_DECLINED_FEEDBACK ORDER BY timestamp DESC LIMIT 1")
46+
@Query("SELECT * from app_enjoyment WHERE eventType = $TYPE_DECLINED_TO_PARTICIPATE AND promptCount = :promptCount")
47+
fun hasUserDeclinedToSayWhetherEnjoying(promptCount: Int): Boolean
48+
49+
@Query("SELECT timestamp FROM app_enjoyment WHERE eventType=$TYPE_DECLINED_RATING OR eventType=$TYPE_DECLINED_FEEDBACK OR eventType=$TYPE_DECLINED_TO_PARTICIPATE ORDER BY timestamp DESC LIMIT 1")
4650
fun latestDateUserDeclinedRatingOrFeedback(): Long?
4751

4852
}
@@ -59,7 +63,8 @@ enum class AppEnjoymentEventType(val value: Int) {
5963
USER_PROVIDED_RATING(TYPE_PROVIDED_RATING),
6064
USER_DECLINED_RATING(TYPE_DECLINED_RATING),
6165
USER_PROVIDED_FEEDBACK(TYPE_PROVIDED_FEEDBACK),
62-
USER_DECLINED_FEEDBACK(TYPE_DECLINED_FEEDBACK);
66+
USER_DECLINED_FEEDBACK(TYPE_DECLINED_FEEDBACK),
67+
USER_DECLINED_TO_SAY_WHETHER_ENJOYING(TYPE_DECLINED_TO_PARTICIPATE);
6368

6469
companion object {
6570
private val map = AppEnjoymentEventType.values().associateBy(AppEnjoymentEventType::value)

0 commit comments

Comments
 (0)