Skip to content

Commit a147c7e

Browse files
cmonfortepmarcosholgado
authored andcommitted
Waitlist for WD (duckduckgo#2831)
Task/Issue URL: https://app.asana.com/0/0/1202843599939263/f ### Description Adds a new waitlist in settings. Similar to the one we had for macOS. ### Steps to test this PR Follow instructions in https://app.asana.com/0/0/1203975737540623/f ### UI changes | Before | After | | ------ | ----- | !(Upload before screenshot)|(Upload after screenshot)| Co-authored-by: Marcos <marcosh@duckduckgo.com>
1 parent 99329d4 commit a147c7e

File tree

61 files changed

+3221
-50
lines changed

Some content is hidden

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

61 files changed

+3221
-50
lines changed

app/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ dependencies {
256256
implementation project(':request-filterer-impl')
257257
implementation project(':request-filterer-store')
258258

259+
implementation project(':windows-api')
260+
implementation project(':windows-impl')
261+
implementation project(':windows-store')
262+
259263
// Deprecated. TODO: Stop using this artifact.
260264
implementation "androidx.legacy:legacy-support-v4:_"
261265
debugImplementation Square.leakCanary.android

app/src/main/java/com/duckduckgo/app/settings/SettingsActivity.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener
2929
import android.widget.Toast
3030
import androidx.annotation.StringRes
3131
import androidx.core.app.NotificationManagerCompat
32+
import androidx.core.view.isVisible
3233
import androidx.lifecycle.Lifecycle
3334
import androidx.lifecycle.flowWithLifecycle
3435
import androidx.lifecycle.lifecycleScope
@@ -79,6 +80,11 @@ import com.duckduckgo.mobile.android.ui.view.listitem.TwoLineListItem
7980
import com.duckduckgo.mobile.android.ui.viewbinding.viewBinding
8081
import com.duckduckgo.mobile.android.vpn.ui.onboarding.VpnOnboardingActivity
8182
import com.duckduckgo.mobile.android.vpn.ui.tracker_activity.DeviceShieldTrackerActivity
83+
import com.duckduckgo.windows.api.WindowsWaitlistState
84+
import com.duckduckgo.windows.api.WindowsWaitlistState.InBeta
85+
import com.duckduckgo.windows.api.WindowsWaitlistState.JoinedWaitlist
86+
import com.duckduckgo.windows.api.WindowsWaitlistState.NotJoinedQueue
87+
import com.duckduckgo.windows.impl.waitlist.ui.WindowsWaitlistActivity
8288
import javax.inject.Inject
8389
import kotlinx.coroutines.flow.launchIn
8490
import kotlinx.coroutines.flow.onEach
@@ -207,6 +213,7 @@ class SettingsActivity : DuckDuckGoActivity() {
207213
emailSetting.setClickListener { viewModel.onEmailProtectionSettingClicked() }
208214
macOsSetting.setClickListener { viewModel.onMacOsSettingClicked() }
209215
vpnSetting.setClickListener { viewModel.onAppTPSettingClicked() }
216+
windowsSetting.setClickListener { viewModel.windowsSettingClicked() }
210217
}
211218
}
212219

@@ -250,6 +257,7 @@ class SettingsActivity : DuckDuckGoActivity() {
250257
)
251258
updateEmailSubtitle(it.emailAddress)
252259
updateAutofill(it.showAutofill)
260+
updateWindowsSettings(it.windowsWaitlistState)
253261
viewsCustomize.notificationsSetting.setSecondaryText(getString(it.notificationsSettingSubtitleId))
254262
}
255263
}.launchIn(lifecycleScope)
@@ -268,6 +276,19 @@ class SettingsActivity : DuckDuckGoActivity() {
268276
}
269277
}
270278

279+
private fun updateWindowsSettings(waitlistState: WindowsWaitlistState?) {
280+
viewsMore.windowsSetting.isVisible = waitlistState != null
281+
282+
with(viewsMore) {
283+
when (waitlistState) {
284+
is InBeta -> windowsSetting.setSecondaryText(getString(R.string.windows_settings_description_ready))
285+
is JoinedWaitlist -> windowsSetting.setSecondaryText(getString(R.string.windows_settings_description_list))
286+
is NotJoinedQueue -> windowsSetting.setSecondaryText(getString(R.string.windows_settings_description))
287+
null -> {}
288+
}
289+
}
290+
}
291+
271292
private fun updateEmailSubtitle(emailAddress: String?) {
272293
val subtitle = emailAddress ?: getString(R.string.settingsEmailProtectionSubtitle)
273294
viewsMore.emailSetting.setSecondaryText(subtitle)
@@ -410,6 +431,7 @@ class SettingsActivity : DuckDuckGoActivity() {
410431
is Command.LaunchMacOs -> launchMacOsScreen()
411432
is Command.LaunchAutoconsent -> launchAutoconsent()
412433
is Command.LaunchNotificationsSettings -> launchNotificationsSettings()
434+
is Command.LaunchWindows -> launchWindowsScreen()
413435
null -> TODO()
414436
}
415437
}
@@ -596,6 +618,11 @@ class SettingsActivity : DuckDuckGoActivity() {
596618
startActivity(MacOsActivity.intent(this), options)
597619
}
598620

621+
private fun launchWindowsScreen() {
622+
val options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
623+
startActivity(WindowsWaitlistActivity.intent(this), options)
624+
}
625+
599626
private fun launchAutoconsent() {
600627
val options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
601628
startActivity(AutoconsentSettingsActivity.intent(this), options)

app/src/main/java/com/duckduckgo/app/settings/SettingsViewModel.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
5050
import com.duckduckgo.mobile.android.vpn.ui.onboarding.VpnStore
5151
import com.duckduckgo.privacy.config.api.Gpc
5252
import com.duckduckgo.privacy.config.api.PrivacyFeatureName
53+
import com.duckduckgo.windows.api.WindowsWaitlist
54+
import com.duckduckgo.windows.api.WindowsWaitlistFeature
55+
import com.duckduckgo.windows.api.WindowsWaitlistState
5356
import javax.inject.Inject
5457
import kotlinx.coroutines.Job
5558
import kotlinx.coroutines.channels.BufferOverflow
@@ -79,6 +82,8 @@ class SettingsViewModel @Inject constructor(
7982
private val autofillCapabilityChecker: AutofillCapabilityChecker,
8083
private val vpnFeaturesRegistry: VpnFeaturesRegistry,
8184
private val autoconsent: Autoconsent,
85+
private val windowsWaitlist: WindowsWaitlist,
86+
private val windowsFeature: WindowsWaitlistFeature,
8287
) : ViewModel(), DefaultLifecycleObserver {
8388

8489
private var deviceShieldStatePollingJob: Job? = null
@@ -101,6 +106,7 @@ class SettingsViewModel @Inject constructor(
101106
val showAutofill: Boolean = false,
102107
val autoconsentEnabled: Boolean = false,
103108
@StringRes val notificationsSettingSubtitleId: Int = R.string.settingsSubtitleNotificationsDisabled,
109+
val windowsWaitlistState: WindowsWaitlistState? = null,
104110
)
105111

106112
data class AutomaticallyClearData(
@@ -133,6 +139,7 @@ class SettingsViewModel @Inject constructor(
133139
data class ShowClearWhenDialog(val option: ClearWhenOption) : Command()
134140
object LaunchMacOs : Command()
135141
object LaunchNotificationsSettings : Command()
142+
object LaunchWindows : Command()
136143
}
137144

138145
private val viewState = MutableStateFlow(ViewState())
@@ -171,6 +178,7 @@ class SettingsViewModel @Inject constructor(
171178
showAutofill = autofillCapabilityChecker.canAccessCredentialManagementScreen(),
172179
autoconsentEnabled = autoconsent.isSettingEnabled(),
173180
notificationsSettingSubtitleId = getNotificationsSettingSubtitleId(notificationsEnabled),
181+
windowsWaitlistState = windowsSettingState(),
174182
),
175183
)
176184
}
@@ -287,6 +295,10 @@ class SettingsViewModel @Inject constructor(
287295
viewModelScope.launch { command.send(Command.LaunchMacOs) }
288296
}
289297

298+
fun windowsSettingClicked() {
299+
viewModelScope.launch { command.send(Command.LaunchWindows) }
300+
}
301+
290302
fun onDefaultBrowserToggled(enabled: Boolean) {
291303
Timber.i("User toggled default browser, is now enabled: $enabled")
292304
val defaultBrowserSelected = defaultWebBrowserCapability.isDefaultBrowser()
@@ -407,6 +419,11 @@ class SettingsViewModel @Inject constructor(
407419
}
408420
}
409421

422+
private fun windowsSettingState(): WindowsWaitlistState? {
423+
if (!windowsFeature.self().isEnabled()) return null
424+
return windowsWaitlist.getWaitlistState()
425+
}
426+
410427
fun onThemeSelected(selectedTheme: DuckDuckGoTheme) {
411428
Timber.d("User toggled theme, theme to set: $selectedTheme")
412429
if (themingDataStore.isCurrentlySelected(selectedTheme)) {

app/src/main/res/layout/content_settings_other.xml

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,28 @@
1414
~ limitations under the License.
1515
-->
1616

17-
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
17+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
1818
xmlns:app="http://schemas.android.com/apk/res-auto"
1919
android:id="@+id/settingsSectionOther"
20+
android:orientation="vertical"
2021
android:layout_width="match_parent"
2122
android:layout_height="wrap_content">
2223

2324
<com.duckduckgo.mobile.android.ui.view.divider.HorizontalDivider
2425
android:id="@+id/settingsOtherDivider"
2526
android:layout_width="match_parent"
26-
android:layout_height="wrap_content"
27-
app:layout_constraintEnd_toEndOf="parent"
28-
app:layout_constraintStart_toStartOf="parent"
29-
app:layout_constraintTop_toTopOf="parent" />
27+
android:layout_height="wrap_content" />
3028

3129
<com.duckduckgo.mobile.android.ui.view.listitem.SectionHeaderListItem
3230
android:id="@+id/settingsOtherTitle"
3331
android:layout_width="match_parent"
3432
android:layout_height="wrap_content"
35-
app:layout_constraintTop_toBottomOf="@id/settingsOtherDivider"
3633
app:primaryText="@string/settingsHeadingMore" />
3734

38-
<com.duckduckgo.mobile.android.ui.view.listitem.TwoLineListItem
39-
android:id="@+id/macOsSetting"
40-
android:layout_width="match_parent"
41-
android:layout_height="wrap_content"
42-
app:layout_constraintEnd_toEndOf="parent"
43-
app:layout_constraintStart_toStartOf="parent"
44-
app:layout_constraintTop_toBottomOf="@id/settingsOtherTitle"
45-
app:primaryText="@string/macos_settings_title"
46-
app:primaryTextTruncated="false"
47-
app:secondaryText="@string/macos_settings_description"
48-
app:showBetaPill="true" />
49-
5035
<com.duckduckgo.mobile.android.ui.view.listitem.TwoLineListItem
5136
android:id="@+id/emailSetting"
5237
android:layout_width="match_parent"
5338
android:layout_height="wrap_content"
54-
app:layout_constraintEnd_toEndOf="parent"
55-
app:layout_constraintStart_toStartOf="parent"
56-
app:layout_constraintTop_toBottomOf="@id/macOsSetting"
5739
app:primaryText="@string/settingsEmailProtectionTitle"
5840
app:primaryTextTruncated="false"
5941
app:secondaryText="@string/settingsEmailProtectionSubtitle"
@@ -63,13 +45,27 @@
6345
android:id="@+id/vpnSetting"
6446
android:layout_width="match_parent"
6547
android:layout_height="wrap_content"
66-
app:layout_constraintEnd_toEndOf="parent"
67-
app:layout_constraintStart_toStartOf="parent"
68-
app:layout_constraintTop_toBottomOf="@id/emailSetting"
6948
app:primaryText="@string/atp_SettingsTitle"
7049
app:primaryTextTruncated="false"
7150
app:secondaryText="@string/atp_SettingsDeviceShieldDisabled"
7251
app:showBetaPill="true" />
7352

53+
<com.duckduckgo.mobile.android.ui.view.listitem.TwoLineListItem
54+
android:id="@+id/macOsSetting"
55+
android:layout_width="match_parent"
56+
android:layout_height="wrap_content"
57+
app:primaryText="@string/macos_settings_title"
58+
app:primaryTextTruncated="false"
59+
app:secondaryText="@string/macos_settings_description"
60+
app:showBetaPill="true" />
61+
62+
<com.duckduckgo.mobile.android.ui.view.listitem.TwoLineListItem
63+
android:id="@+id/windowsSetting"
64+
android:layout_width="match_parent"
65+
android:layout_height="wrap_content"
66+
app:primaryText="@string/windows_settings_title"
67+
app:primaryTextTruncated="false"
68+
app:secondaryText="@string/windows_settings_description"
69+
app:showBetaPill="true" />
7470

75-
</androidx.constraintlayout.widget.ConstraintLayout>
71+
</LinearLayout>

app/src/main/res/values/donottranslate.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@
2828
<!-- We won't have a translation for this, to be used later. -->
2929
<string name="trackersOverviewEmpty">No tracking requests were blocked from loading on this page. If a company\'s requests are loaded, it can allow them to profile you.</string>
3030

31+
<!-- Windows Settings -->
32+
<string name="windows_settings_title">DuckDuckGo Windows App</string>
33+
<string name="windows_settings_description">Browse privately with our app for Windows</string>
34+
<string name="windows_settings_description_ready">Download available</string>
35+
<string name="windows_settings_description_list">You\'re on the list!</string>
3136
</resources>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@
619619

620620
<!-- MacOs Settings -->
621621
<string name="macos_settings_description">Browse privately with our app for Mac</string>
622-
<string name="macos_settings_title">DuckDuckGo Desktop App</string>
622+
<string name="macos_settings_title">DuckDuckGo Mac App</string>
623623

624624
<!-- Downloads -->
625625
<string name="downloadsMenuItem">Downloads</string>

app/src/test/java/com/duckduckgo/app/settings/SettingsViewModelTest.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import com.duckduckgo.appbuildconfig.api.AppBuildConfig
3939
import com.duckduckgo.autoconsent.api.Autoconsent
4040
import com.duckduckgo.autofill.api.AutofillCapabilityChecker
4141
import com.duckduckgo.feature.toggles.api.FeatureToggle
42+
import com.duckduckgo.feature.toggles.api.Toggle
4243
import com.duckduckgo.mobile.android.ui.DuckDuckGoTheme
4344
import com.duckduckgo.mobile.android.ui.store.ThemingDataStore
4445
import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
@@ -47,6 +48,9 @@ import com.duckduckgo.mobile.android.vpn.feature.AppTpSetting
4748
import com.duckduckgo.mobile.android.vpn.ui.onboarding.VpnStore
4849
import com.duckduckgo.privacy.config.api.Gpc
4950
import com.duckduckgo.privacy.config.api.PrivacyFeatureName
51+
import com.duckduckgo.windows.api.WindowsWaitlist
52+
import com.duckduckgo.windows.api.WindowsWaitlistFeature
53+
import com.duckduckgo.windows.api.WindowsWaitlistState
5054
import kotlin.time.ExperimentalTime
5155
import kotlinx.coroutines.ExperimentalCoroutinesApi
5256
import kotlinx.coroutines.test.runTest
@@ -113,13 +117,22 @@ class SettingsViewModelTest {
113117
@Mock
114118
private lateinit var appTpFeatureConfig: AppTpFeatureConfig
115119

120+
@Mock
121+
private lateinit var windowsWaitlist: WindowsWaitlist
122+
123+
@Mock
124+
private lateinit var windowsFeatureToggle: Toggle
125+
116126
@get:Rule
117127
val coroutineTestRule: CoroutineTestRule = CoroutineTestRule()
118128

119129
@Before
120130
fun before() {
121131
MockitoAnnotations.openMocks(this)
122132

133+
val windowsFeature: WindowsWaitlistFeature = mock()
134+
whenever(windowsFeatureToggle.isEnabled()).thenReturn(false)
135+
whenever(windowsFeature.self()).thenReturn(windowsFeatureToggle)
123136
whenever(appTpFeatureConfig.isEnabled(AppTpSetting.OpenBeta)).thenReturn(false)
124137
whenever(mockAppSettingsDataStore.automaticallyClearWhenOption).thenReturn(APP_EXIT_ONLY)
125138
whenever(mockAppSettingsDataStore.automaticallyClearWhatOption).thenReturn(CLEAR_NONE)
@@ -129,6 +142,7 @@ class SettingsViewModelTest {
129142
whenever(mockVariantManager.getVariant()).thenReturn(VariantManager.DEFAULT_VARIANT)
130143
whenever(mockAppBuildConfig.versionName).thenReturn("name")
131144
whenever(mockAppBuildConfig.versionCode).thenReturn(1)
145+
whenever(windowsWaitlist.getWaitlistState()).thenReturn(WindowsWaitlistState.NotJoinedQueue)
132146

133147
testee = SettingsViewModel(
134148
mockThemeSettingsDataStore,
@@ -145,6 +159,8 @@ class SettingsViewModelTest {
145159
autofillCapabilityChecker,
146160
vpnFeaturesRegistry,
147161
autoconsent,
162+
windowsWaitlist,
163+
windowsFeature,
148164
)
149165

150166
runTest {
@@ -739,6 +755,17 @@ class SettingsViewModelTest {
739755
}
740756
}
741757

758+
@Test
759+
fun whenWindowsSettingClickedThenEmitCommandLaunchWindows() = runTest {
760+
testee.commands().test {
761+
testee.windowsSettingClicked()
762+
763+
assertEquals(Command.LaunchWindows, awaitItem())
764+
765+
cancelAndConsumeRemainingEvents()
766+
}
767+
}
768+
742769
@Test
743770
fun whenUserRequestedToChangeNotificationsSettingThenEmitLaunchNotificationsSettingsAndSendPixel() = runTest {
744771
testee.commands().test {

common-ui/src/main/java/com/duckduckgo/mobile/android/ui/view/TextExtensions.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import android.view.View
3030
import android.widget.TextView
3131
import androidx.annotation.AttrRes
3232
import androidx.annotation.ColorInt
33-
import androidx.core.content.ContextCompat
3433
import com.duckduckgo.mobile.android.R
3534
import com.duckduckgo.mobile.android.ui.spans.DuckDuckGoClickableSpan
3635

@@ -82,7 +81,7 @@ fun TextView.addClickableLink(
8281
)
8382
setSpan(
8483
ForegroundColorSpan(
85-
ContextCompat.getColor(context, R.color.cornflowerBlue),
84+
context.getColorFromAttr(R.attr.daxColorAccentBlue),
8685
),
8786
fullText.getSpanStart(it),
8887
fullText.getSpanEnd(it),
@@ -114,7 +113,7 @@ fun TextView.addClickableSpan(
114113
)
115114
setSpan(
116115
ForegroundColorSpan(
117-
ContextCompat.getColor(context, R.color.cornflowerBlue),
116+
context.getColorFromAttr(R.attr.daxColorAccentBlue),
118117
),
119118
fullText.getSpanStart(it),
120119
fullText.getSpanEnd(it),

0 commit comments

Comments
 (0)