Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bb6d6d3
Add permission required to create home screen shortcuts
CDRussell Jun 15, 2018
067b1d5
Add class to allow downloading favicons
CDRussell Jun 15, 2018
867896c
Add "add to home" functionality as a first pass.
CDRussell Jun 15, 2018
19bbeab
Choose better shortcut title; appropriately enable/disable menu item
CDRussell Jun 15, 2018
582cf2c
Merge branch 'develop' of github.com:duckduckgo/Android into feature/…
CDRussell Jul 3, 2018
a4d0b97
Add support for two distinct layout states; browser and blank tab
CDRussell Jul 3, 2018
b0407a3
Merge branch 'develop' of github.com:duckduckgo/Android into feature/…
CDRussell Jul 3, 2018
4a562d9
Merge branch 'develop' of github.com:duckduckgo/Android into feature/…
CDRussell Jul 9, 2018
bfddf89
Further work on UX of call to action; wired to show screen when clicked
CDRussell Jul 9, 2018
5b3db5a
Rename "time based" feature analyzer class to something more generic
CDRussell Jul 10, 2018
7b2afb1
Add in feature for home screen call to action
CDRussell Jul 10, 2018
e12c5de
Only show call to action at appropriate time for active variants
CDRussell Jul 10, 2018
bf11865
Remove "add to home" commits
CDRussell Jul 10, 2018
2bf3f67
Improve testing to ensure user dismissing c2a doesn't impact banner
CDRussell Jul 10, 2018
cd0b123
Add static import for better readability
CDRussell Jul 10, 2018
b9ea6c0
Undoing rename of dagger parameter
CDRussell Jul 10, 2018
780937f
Remove unused function
CDRussell Jul 10, 2018
ff4db6f
Add light ripple effect to call to action
CDRussell Jul 10, 2018
dd7ae62
Code formatting
CDRussell Jul 10, 2018
f1635ea
Adding activity transition for call to action
CDRussell Jul 10, 2018
056d52b
Upgrading tooling
CDRussell Jul 11, 2018
c51d702
Add fix to ensure only Nougat or newer devices get experiment
CDRussell Jul 11, 2018
1c5739e
Fix centering of call to action button
CDRussell Jul 12, 2018
58f6d7e
Add correct menu reference for tools:menu link
CDRussell Jul 12, 2018
e82efca
Move new tab layout into its own layout file
CDRussell Jul 13, 2018
812eeae
Programmatically hide DDG logo when there's not enough space for it
CDRussell Jul 13, 2018
a7f3a66
Layout tweaks around call to action button
CDRussell Jul 16, 2018
d9f1ab2
Add @Synchronized to getVariant to prevent concurrent allocations
CDRussell Jul 16, 2018
9aa4876
Tweaking animations and minor UI changes to call to action
CDRussell Jul 17, 2018
d025a2c
Formatting
CDRussell Jul 17, 2018
c134469
Fix ddgLogo visibility problem after setting DDG as default browser
CDRussell Jul 18, 2018
9f0ca08
Add comment to an otherwise unclear piece of code
CDRussell Jul 18, 2018
82bc1d9
Add new default browser illustrations
CDRussell Jul 18, 2018
9d8dfd2
Configure variants
CDRussell Jul 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.duckduckgo.app.browser.defaultBrowsing
import com.duckduckgo.app.global.install.AppInstallStore
import com.duckduckgo.app.statistics.Variant
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.VariantManager.VariantFeature.DefaultBrowserFeature.ShowTimedReminder
import com.duckduckgo.app.statistics.VariantManager.VariantFeature.DefaultBrowserFeature.ShowBanner
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import org.junit.Assert.assertFalse
Expand All @@ -28,7 +28,7 @@ import org.junit.Before
import org.junit.Test
import java.util.concurrent.TimeUnit

class DefaultBrowserTimeBasedNotificationTest {
class DefaultBrowserBannerNotificationTest {

private lateinit var testee: DefaultBrowserTimeBasedNotification

Expand All @@ -44,48 +44,55 @@ class DefaultBrowserTimeBasedNotificationTest {
@Test
fun whenDefaultBrowserNotSupportedByDeviceThenNotificationNotShown() {
configureEnvironment(false, true, true, false)
assertFalse(testee.shouldShowNotification(browserShowing = true))
assertFalse(testee.shouldShowBannerNotification(browserShowing = true))
}

@Test
fun whenDefaultBrowserFeatureNotSupportedThenNotificationNotShown() {
configureEnvironment(true, false, true, false)
assertFalse(testee.shouldShowNotification(browserShowing = true))
assertFalse(testee.shouldShowBannerNotification(browserShowing = true))
}

@Test
fun whenNoAppInstallTimeRecordedThenNotificationNotShown() {
configureEnvironment(true, true, false, false)
assertFalse(testee.shouldShowNotification(browserShowing = true))
assertFalse(testee.shouldShowBannerNotification(browserShowing = true))
}

@Test
fun whenUserDeclinedPreviouslyThenNotificationNotShown() {
configureEnvironment(true, true, true, true)
assertFalse(testee.shouldShowNotification(browserShowing = true))
assertFalse(testee.shouldShowBannerNotification(browserShowing = true))
}

@Test
fun whenNotEnoughTimeHasPassedSinceInstallThenNotificationNotShown() {
configureEnvironment(true, true, true, false)
whenever(appInstallStore.installTimestamp).thenReturn(0)
assertFalse(testee.shouldShowNotification(browserShowing = true, timeNow = TimeUnit.SECONDS.toMillis(10)))
assertFalse(testee.shouldShowBannerNotification(browserShowing = true, timeNow = TimeUnit.SECONDS.toMillis(10)))
}

@Test
fun whenEnoughTimeHasPassedSinceInstallThenNotificationShown() {
configureEnvironment(true, true, true, false)
whenever(appInstallStore.installTimestamp).thenReturn(0)
assertTrue(testee.shouldShowNotification(browserShowing = true, timeNow = TimeUnit.DAYS.toMillis(100)))
assertTrue(testee.shouldShowBannerNotification(browserShowing = true, timeNow = TimeUnit.DAYS.toMillis(100)))
}

@Test
fun whenUserDeclinedHomeScreenCallToActionPreviouslyThenNotificationStillShown() {
configureEnvironment(true, true, true, false)
whenever(appInstallStore.hasUserDeclinedDefaultBrowserHomeScreenCallToActionPreviously()).thenReturn(true)
assertTrue(testee.shouldShowBannerNotification(browserShowing = true))
}

private fun configureEnvironment(deviceSupported: Boolean, featureEnabled: Boolean, timestampRecorded: Boolean, previousDecline: Boolean) {
whenever(mockDetector.deviceSupportsDefaultBrowserConfiguration()).thenReturn(deviceSupported)
whenever(variantManager.getVariant()).thenReturn(if (featureEnabled) variantWithFeatureEnabled() else variantWithFeatureDisabled())
whenever(appInstallStore.hasInstallTimestampRecorded()).thenReturn(timestampRecorded)
whenever(appInstallStore.hasUserDeclinedDefaultBrowserPreviously()).thenReturn(previousDecline)
whenever(appInstallStore.hasUserDeclinedDefaultBrowserBannerPreviously()).thenReturn(previousDecline)
}

private fun variantWithFeatureEnabled() = Variant("", 0.0, listOf(ShowTimedReminder))
private fun variantWithFeatureEnabled() = Variant("", 0.0, listOf(ShowBanner))
private fun variantWithFeatureDisabled() = Variant("", 0.0, listOf())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2018 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.app.browser.defaultBrowsing

import com.duckduckgo.app.global.install.AppInstallStore
import com.duckduckgo.app.statistics.Variant
import com.duckduckgo.app.statistics.VariantManager
import com.duckduckgo.app.statistics.VariantManager.VariantFeature.DefaultBrowserFeature.ShowHomeScreenCallToAction
import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test

class DefaultBrowserHomeScreenCallToActionTest {

private lateinit var testee: DefaultBrowserTimeBasedNotification

private val mockDetector: DefaultBrowserDetector = mock()
private val appInstallStore: AppInstallStore = mock()
private val variantManager: VariantManager = mock()

@Before
fun setup() {
testee = DefaultBrowserTimeBasedNotification(mockDetector, appInstallStore, variantManager)
}

@Test
fun whenDefaultBrowserNotSupportedByDeviceThenCallToActionNotShown() {
configureEnvironment(false, true, true, false)
assertFalse(testee.shouldShowHomeScreenCallToActionNotification())
}

@Test
fun whenDefaultBrowserFeatureNotSupportedThenCallToActionNotShown() {
configureEnvironment(true, false, true, false)
assertFalse(testee.shouldShowHomeScreenCallToActionNotification( ))
}

@Test
fun whenNoAppInstallTimeRecordedThenCallToActionNotShown() {
configureEnvironment(true, true, false, false)
assertFalse(testee.shouldShowHomeScreenCallToActionNotification( ))
}

@Test
fun whenUserDeclinedPreviouslyThenCallToActionNotShown() {
configureEnvironment(true, true, true, true)
assertFalse(testee.shouldShowHomeScreenCallToActionNotification( ))
}

@Test
fun whenAllOtherConditionsPassThenCallToActionShown() {
configureEnvironment(true, true, true, false)
whenever(appInstallStore.installTimestamp).thenReturn(0)
assertTrue(testee.shouldShowHomeScreenCallToActionNotification())
}

private fun configureEnvironment(deviceSupported: Boolean, featureEnabled: Boolean, timestampRecorded: Boolean, previousDecline: Boolean) {
whenever(mockDetector.deviceSupportsDefaultBrowserConfiguration()).thenReturn(deviceSupported)
whenever(variantManager.getVariant()).thenReturn(if (featureEnabled) variantWithFeatureEnabled() else variantWithFeatureDisabled())
whenever(appInstallStore.hasInstallTimestampRecorded()).thenReturn(timestampRecorded)
whenever(appInstallStore.hasUserDeclinedDefaultBrowserHomeScreenCallToActionPreviously()).thenReturn(previousDecline)
}

private fun variantWithFeatureEnabled() = Variant("", 0.0, listOf(ShowHomeScreenCallToAction))
private fun variantWithFeatureDisabled() = Variant("", 0.0, listOf())
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ class AppInstallSharedPreferencesTest {
}

@Test
fun whenInitializedThenUserHasNotBeenMarkedAsHavingPreviouslyDeclined() {
assertFalse(testee.hasUserDeclinedDefaultBrowserPreviously())
fun whenInitializedThenUserHasNotBeenMarkedAsHavingPreviouslyDeclinedBanner() {
assertFalse(testee.hasUserDeclinedDefaultBrowserBannerPreviously())
}

@Test
fun whenInitializedThenUserHasNotBeenMarkedAsHavingPreviouslyDeclinedHomeScreenCallToAction() {
assertFalse(testee.hasUserDeclinedDefaultBrowserHomeScreenCallToActionPreviously())
}

@Test
Expand All @@ -60,15 +65,17 @@ class AppInstallSharedPreferencesTest {
}

@Test
fun whenUserPreviouslyDeclinedThenThatIsReturnedWhenQueried() {
fun whenUserPreviouslyDeclinedBannerThenThatIsReturnedWhenQueried() {
val timestamp = 1L
testee.recordUserDeclinedToSetDefaultBrowser(timestamp)
assertTrue(testee.hasUserDeclinedDefaultBrowserPreviously())
testee.recordUserDeclinedBannerToSetDefaultBrowser(timestamp)
assertTrue(testee.hasUserDeclinedDefaultBrowserBannerPreviously())
}

@Test
fun whenUserNotPreviouslyDeclinedThenThatIsReturnedWhenQueried() {
assertFalse(testee.hasUserDeclinedDefaultBrowserPreviously())
fun whenUserPreviouslyDeclinedHomeScreenCallToActionThenThatIsReturnedWhenQueried() {
val timestamp = 1L
testee.recordUserDeclinedHomeScreenCallToActionToSetDefaultBrowser(timestamp)
assertTrue(testee.hasUserDeclinedDefaultBrowserHomeScreenCallToActionPreviously())
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.duckduckgo.app.statistics

import android.os.Build
import android.support.test.filters.SdkSuppress
import com.duckduckgo.app.statistics.store.StatisticsDataStore
import com.nhaarman.mockito_kotlin.*
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -93,7 +95,8 @@ class ExperimentationVariantManagerTest {


@Test
fun whenNoVariantPersistedThenNewVariantAllocated() {
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
fun whenNougatOrLaterAndNoVariantPersistedThenNewVariantAllocated() {
activeVariants.add(Variant("foo", 100.0))

testee.getVariant(activeVariants)
Expand All @@ -102,11 +105,30 @@ class ExperimentationVariantManagerTest {
}

@Test
fun whenNoVariantPersistedThenNewVariantKeyIsAllocatedAndPersisted() {
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
fun whenNougatOrLaterAndNoVariantPersistedThenNewVariantKeyIsAllocatedAndPersisted() {
activeVariants.add(Variant("foo", 100.0))

testee.getVariant(activeVariants)

verify(mockStore).variant = "foo"
}

@Test
@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
fun whenMarshmallowOrEarlierAndNoVariantPersistedThenDefaultVariantAllocated() {
activeVariants.add(Variant("foo", 100.0))

assertEquals(VariantManager.DEFAULT_VARIANT, testee.getVariant(activeVariants))
}

@Test
@SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
fun whenMarshmallowOrEarlierAndNoVariantPersistedThenDefaultVariantKeyIsAllocatedAndPersisted() {
activeVariants.add(Variant("foo", 100.0))

testee.getVariant(activeVariants)

verify(mockStore).variant = VariantManager.DEFAULT_VARIANT.key
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

package com.duckduckgo.app.statistics

import org.junit.Assert.assertNotNull
import org.junit.Assert.fail
import com.duckduckgo.app.statistics.VariantManager.VariantFeature.DefaultBrowserFeature.*
import org.junit.Assert.*
import org.junit.Test

class VariantManagerTest {
Expand All @@ -26,24 +26,43 @@ class VariantManagerTest {
private val totalWeight = variants.sumByDouble { it.weight }

@Test
fun whenChanceOfControlVariantCalculatedThenOddsAreOneInTwo() {
val variant = variants.firstOrNull { it.key == "my" }
assertNotNull(variant)
assertEqualsDouble( 0.5, variant!!.weight / totalWeight)
fun onboardingOnlyVariantConfiguredCorrectly() {
val variant = variants.firstOrNull { it.key == "ms" }
assertEqualsDouble( 0.20, variant!!.weight / totalWeight)
assertTrue(variant.hasFeature(ShowInOnboarding))
assertEquals(1, variant.features.size)
}

@Test
fun homeScreenCallToActionVariantConfiguredCorrectly() {
val variant = variants.firstOrNull { it.key == "mt" }
assertEqualsDouble( 0.20, variant!!.weight / totalWeight)
assertTrue(variant.hasFeature(ShowHomeScreenCallToAction))
assertEquals(1, variant.features.size)
}

@Test
fun whenChanceOfOnboardingOnlyVariantCalculatedThenOddsAreOneInFour() {
val variant = variants.firstOrNull { it.key == "mw" }
assertNotNull(variant)
assertEqualsDouble( 0.25, variant!!.weight / totalWeight)
fun showBannerVariantConfiguredCorrectly() {
val variant = variants.firstOrNull { it.key == "mu" }
assertEqualsDouble( 0.20, variant!!.weight / totalWeight)
assertTrue(variant.hasFeature(ShowBanner))
assertEquals(1, variant.features.size)
}

@Test
fun whenChanceOfOnboardingAndReminderVariantCalculatedThenOddsAreOneInFour() {
val variant = variants.firstOrNull { it.key == "mx" }
assertNotNull(variant)
assertEqualsDouble( 0.25, variant!!.weight / totalWeight)
fun showBannerAndShowHomeScreenCallToActionVariantConfiguredCorrectly() {
val variant = variants.firstOrNull { it.key == "mv" }
assertEqualsDouble( 0.20, variant!!.weight / totalWeight)
assertTrue(variant.hasFeature(ShowBanner))
assertTrue(variant.hasFeature(ShowHomeScreenCallToAction))
assertEquals(2, variant.features.size)
}

@Test
fun controlVariantConfiguredCorrectly() {
val variant = variants.firstOrNull { it.key == "my" }
assertEqualsDouble( 0.2, variant!!.weight / totalWeight)
assertEquals(0, variant.features.size)
}

private fun assertEqualsDouble(expected: Double, actual: Double) {
Expand Down
Loading