Skip to content

Commit

Permalink
Merge branch 'hotfix/5.207.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
karlenDimla committed Jul 3, 2024
2 parents ae9b877 + 4e1586c commit b03ab19
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 385 deletions.
2 changes: 1 addition & 1 deletion app/version/version.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION=5.207.1
VERSION=5.207.2
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
android:exported="false" />
<activity
android:name=".settings.geoswitching.NetpGeoswitchingActivity"
android:configChanges="orientation|screenSize"
android:label="@string/netpGeoswitchingTitle"
android:parentActivityName="settings.NetPVpnSettingsActivity"
android:exported="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,8 @@ import com.duckduckgo.anvil.annotations.ContributesViewModel
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.networkprotection.api.NetworkProtectionState
import com.duckduckgo.networkprotection.impl.R
import com.duckduckgo.networkprotection.impl.configuration.WgServerDebugProvider
import com.duckduckgo.networkprotection.impl.pixels.NetworkProtectionPixels
import com.duckduckgo.networkprotection.impl.settings.geoswitching.GeoswitchingListItem.CountryItem
import com.duckduckgo.networkprotection.impl.settings.geoswitching.GeoswitchingListItem.DividerItem
import com.duckduckgo.networkprotection.impl.settings.geoswitching.GeoswitchingListItem.HeaderItem
import com.duckduckgo.networkprotection.impl.settings.geoswitching.GeoswitchingListItem.RecommendedItem
import com.duckduckgo.networkprotection.store.NetPGeoswitchingRepository
import com.duckduckgo.networkprotection.store.NetPGeoswitchingRepository.UserPreferredLocation
import javax.inject.Inject
Expand All @@ -55,17 +50,21 @@ class NetpGeoSwitchingViewModel @Inject constructor(
internal fun viewState(): Flow<ViewState> = viewState.asStateFlow()

internal data class ViewState(
val items: List<GeoswitchingListItem> = emptyList(),
val currentSelectedCountry: String? = null,
val items: List<CountryItem> = emptyList(),
)

data class CountryItem(
val countryCode: String,
val countryEmoji: String,
val countryName: String,
val cities: List<String>,
)

private var initialPreferredLocation: UserPreferredLocation? = null
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
networkProtectionPixels.reportGeoswitchingScreenShown()
}

override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
viewModelScope.launch(dispatcherProvider.io()) {
initialPreferredLocation = netPGeoswitchingRepository.getUserPreferredLocation()
val countryItems = egressServersProvider.getServerLocations().map {
Expand All @@ -77,32 +76,21 @@ class NetpGeoSwitchingViewModel @Inject constructor(
)
}.sortedBy { it.countryName }

val completeList = mutableListOf(
HeaderItem(R.string.netpGeoswitchingHeaderRecommended),
RecommendedItem(
title = R.string.netpGeoswitchingDefaultTitle,
subtitle = R.string.netpGeoswitchingDefaultSubtitle,
),
).apply {
if (countryItems.isNotEmpty()) {
this.add(DividerItem)
this.add(HeaderItem(R.string.netpGeoswitchingHeaderCustom))
this.addAll(countryItems)
} else {
networkProtectionPixels.reportGeoswitchingNoLocations()
}
if (countryItems.isEmpty()) {
networkProtectionPixels.reportGeoswitchingNoLocations()
}

viewState.emit(
ViewState(
items = completeList,
currentSelectedCountry = getSelectedCountryCode(),
items = countryItems,
),
)
}
}

override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
viewModelScope.launch(dispatcherProvider.io()) {
val newPreferredLocation = netPGeoswitchingRepository.getUserPreferredLocation()
if (networkProtectionState.isEnabled()) {
Expand All @@ -121,7 +109,7 @@ class NetpGeoSwitchingViewModel @Inject constructor(
}
}

fun getSelectedCountryCode(): String? {
private fun getSelectedCountryCode(): String? {
return runBlocking { netPGeoswitchingRepository.getUserPreferredLocation().countryCode }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,25 @@
package com.duckduckgo.networkprotection.impl.settings.geoswitching

import android.os.Bundle
import android.view.LayoutInflater
import android.widget.CompoundButton
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.duckduckgo.anvil.annotations.ContributeToActivityStarter
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.common.ui.DuckDuckGoActivity
import com.duckduckgo.common.ui.view.gone
import com.duckduckgo.common.ui.view.show
import com.duckduckgo.common.ui.viewbinding.viewBinding
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.navigation.api.GlobalActivityStarter
import com.duckduckgo.networkprotection.impl.R
import com.duckduckgo.networkprotection.impl.databinding.ActivityNetpGeoswitchingBinding
import com.duckduckgo.networkprotection.impl.databinding.ItemGeoswitchingCountryBinding
import com.duckduckgo.networkprotection.impl.settings.geoswitching.NetpGeoSwitchingViewModel.CountryItem
import com.duckduckgo.networkprotection.impl.settings.geoswitching.NetpGeoSwitchingViewModel.ViewState
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

Expand All @@ -36,13 +44,12 @@ import kotlinx.coroutines.flow.onEach
class NetpGeoswitchingActivity : DuckDuckGoActivity() {
private val binding: ActivityNetpGeoswitchingBinding by viewBinding()
private val viewModel: NetpGeoSwitchingViewModel by bindViewModel()
private lateinit var adapter: NetpGeoswitchingAdapter
private lateinit var lastSelectedButton: CompoundButton

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setupToolbar(binding.includeToolbar.toolbar)
bindViews()
observeViewModel()
lifecycle.addObserver(viewModel)
}
Expand All @@ -52,39 +59,118 @@ class NetpGeoswitchingActivity : DuckDuckGoActivity() {
lifecycle.removeObserver(viewModel)
}

private fun bindViews() {
adapter = NetpGeoswitchingAdapter(
viewModel.getSelectedCountryCode(),
onItemMenuClicked = { country, cities ->
NetpGeoswitchingCityChoiceDialogFragment.instance(
country,
ArrayList(cities),
).show(supportFragmentManager, TAG_DIALOG_CITY_CHOICE)
},
onCountrySelected = {
viewModel.onCountrySelected(it)
},
onNearestAvailableSelected = {
viewModel.onNearestAvailableCountrySelected()
},
)
binding.geoswitchingRecycler.adapter = adapter
private fun onItemMenuClicked(
country: String,
cities: List<String>,
) {
NetpGeoswitchingCityChoiceDialogFragment.instance(
country,
ArrayList(cities),
).show(supportFragmentManager, TAG_DIALOG_CITY_CHOICE)
}

private fun observeViewModel() {
viewModel.viewState()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.distinctUntilChanged()
.onEach { renderViewState(it) }
.launchIn(lifecycleScope)
}

private fun renderViewState(viewState: ViewState) {
adapter.submitList(viewState.items)
bindRecommendedItem(viewState.currentSelectedCountry)

if (viewState.items.isEmpty()) {
binding.customListHeader.gone()
} else {
binding.customListHeader.show()
}

viewState.items.forEach {
val itemBinding = ItemGeoswitchingCountryBinding.inflate(
LayoutInflater.from(binding.geoswitchingList.context),
binding.geoswitchingList,
false,
)

it.bindLocationItem(itemBinding, viewState.currentSelectedCountry)
binding.geoswitchingList.addView(itemBinding.root)
}
}

private fun bindRecommendedItem(currentSelectedCountryCode: String?) {
with(binding.recommendedLocationItem) {
// Sets initial state
this.radioButton.isChecked = currentSelectedCountryCode.isNullOrEmpty()

if (currentSelectedCountryCode.isNullOrEmpty()) {
lastSelectedButton = this.radioButton
}

this.radioButton.setOnCheckedChangeListener { view, isChecked ->
if (isChecked && view != lastSelectedButton) {
lastSelectedButton.isChecked = false
lastSelectedButton = view
viewModel.onNearestAvailableCountrySelected()
}
}
// Automatically selects the country when the item is clicked
this.setClickListener {
this.radioButton.isChecked = true
}
}
}

private fun CountryItem.bindLocationItem(
itemBinding: ItemGeoswitchingCountryBinding,
currentSelectedCountryCode: String?,
) {
// Sets initial state
itemBinding.root.radioButton.isChecked = currentSelectedCountryCode == this.countryCode
if (currentSelectedCountryCode == this.countryCode) {
lastSelectedButton = itemBinding.root.radioButton
}

itemBinding.root.setPrimaryText(this.countryName)
itemBinding.root.setLeadingEmojiIcon(countryEmoji)

if (cities.size > 1) {
itemBinding.root.setSecondaryText(
String.format(
this@NetpGeoswitchingActivity.getString(R.string.netpGeoswitchingHeaderCountrySubtitle),
cities.size,
),
)
itemBinding.root.trailingIconContainer.show()
itemBinding.root.setTrailingIconClickListener {
// Automatically select the country before the user can choose the specific city
itemBinding.root.radioButton.isChecked = true
onItemMenuClicked(countryName, cities)
}
} else {
itemBinding.root.secondaryText.gone()
itemBinding.root.trailingIconContainer.gone()
}

itemBinding.root.radioButton.setOnCheckedChangeListener { view, isChecked ->
if (isChecked && view != lastSelectedButton) {
lastSelectedButton.isChecked = false
lastSelectedButton = view
viewModel.onCountrySelected(this.countryCode)
}
}

// Automatically selects the country when the item is clicked
itemBinding.root.setClickListener {
itemBinding.root.radioButton.isChecked = true
}
}

companion object {
private const val TAG_DIALOG_CITY_CHOICE = "DIALOG_CITY_CHOICE"
}
}

internal object NetpGeoswitchingScreenNoParams : GlobalActivityStarter.ActivityParams
internal object NetpGeoswitchingScreenNoParams : GlobalActivityStarter.ActivityParams {
private fun readResolve(): Any = NetpGeoswitchingScreenNoParams
}
Loading

0 comments on commit b03ab19

Please sign in to comment.