Skip to content

Commit 4ebf22b

Browse files
Merge branch 'main' into On-this-day-refactor
2 parents 0ff7060 + d87b6b9 commit 4ebf22b

File tree

67 files changed

+2755
-286
lines changed

Some content is hidden

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

67 files changed

+2755
-286
lines changed

.github/workflows/android.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
build_alpha:
99
runs-on: ubuntu-latest
1010
steps:
11-
- uses: actions/checkout@v4
11+
- uses: actions/checkout@v5
1212
- uses: actions/setup-java@v4
1313
with:
1414
distribution: 'temurin'

.github/workflows/android_branch.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
build_branch:
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v4
12+
- uses: actions/checkout@v5
1313
- uses: actions/setup-java@v4
1414
with:
1515
distribution: 'temurin'

.github/workflows/android_offline_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
timeout-minutes: 30
1212

1313
steps:
14-
- uses: actions/checkout@v4
14+
- uses: actions/checkout@v5
1515
- uses: actions/setup-java@v4
1616
with:
1717
distribution: 'temurin'

.github/workflows/android_pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
test_pr:
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v4
12+
- uses: actions/checkout@v5
1313
- uses: gradle/actions/wrapper-validation@v4
1414
- uses: actions/setup-java@v4
1515
with:

.github/workflows/android_smoke_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
timeout-minutes: 30
1212

1313
steps:
14-
- uses: actions/checkout@v4
14+
- uses: actions/checkout@v5
1515
- uses: actions/setup-java@v4
1616
with:
1717
distribution: 'temurin'

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ android {
3838
applicationId 'org.wikipedia'
3939
minSdk 21
4040
targetSdk 35
41-
versionCode 50543
41+
versionCode 50544
4242
testApplicationId 'org.wikipedia.test'
4343
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
4444
testInstrumentationRunnerArguments clearPackageData: 'true'

app/src/extra/java/org/wikipedia/donate/GooglePayActivity.kt

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class GooglePayActivity : BaseActivity() {
4646
private var shouldWatchText = true
4747
private var typedManually = false
4848

49-
private val transactionFee get() = max(getAmountFloat(binding.donateAmountText.text.toString()) * GooglePayComponent.TRANSACTION_FEE_PERCENTAGE, viewModel.transactionFee)
49+
private val transactionFee get() = max(DonateUtil.getAmountFloat(binding.donateAmountText.text.toString()) * GooglePayComponent.TRANSACTION_FEE_PERCENTAGE, viewModel.transactionFee)
5050

5151
public override fun onCreate(savedInstanceState: Bundle?) {
5252
super.onCreate(savedInstanceState)
@@ -56,7 +56,7 @@ class GooglePayActivity : BaseActivity() {
5656
supportActionBar?.setDisplayHomeAsUpEnabled(true)
5757
title = ""
5858

59-
binding.donateAmountInput.prefixText = viewModel.currencySymbol
59+
binding.donateAmountInput.prefixText = DonateUtil.currencySymbol
6060

6161
paymentsClient = GooglePayComponent.createPaymentsClient(this)
6262

@@ -82,10 +82,12 @@ class GooglePayActivity : BaseActivity() {
8282
onContentsReceived(resource.data)
8383
}
8484
is GooglePayViewModel.DonateSuccess -> {
85-
DonorExperienceEvent.logAction("impression", "gpay_processed", campaignId = intent.getStringExtra(DonateDialog.ARG_CAMPAIGN_ID).orEmpty())
85+
DonorExperienceEvent.logAction("impression", "gpay_processed",
86+
campaignId = intent.getStringExtra(DonateDialog.ARG_CAMPAIGN_ID).orEmpty().ifEmpty { CAMPAIGN_ID_APP_MENU }
87+
)
8688
CampaignCollection.addDonationResult(
8789
amount = viewModel.finalAmount,
88-
currency = viewModel.currencyCode,
90+
currency = DonateUtil.currencyCode,
8991
recurring = binding.checkBoxRecurring.isChecked
9092
)
9193
setResult(RESULT_OK)
@@ -107,7 +109,7 @@ class GooglePayActivity : BaseActivity() {
107109
return@setOnClickListener
108110
}
109111

110-
var totalAmount = getAmountFloat(amountText)
112+
var totalAmount = DonateUtil.getAmountFloat(amountText)
111113
if (binding.checkBoxTransactionFee.isChecked) {
112114
totalAmount += transactionFee
113115
}
@@ -133,7 +135,7 @@ class GooglePayActivity : BaseActivity() {
133135
}
134136
val buttonToHighlight = binding.amountPresetsContainer.children.firstOrNull { child ->
135137
if (child is MaterialButton) {
136-
val amount = getAmountFloat(text.toString())
138+
val amount = DonateUtil.getAmountFloat(text.toString())
137139
child.tag == amount
138140
} else {
139141
false
@@ -164,18 +166,20 @@ class GooglePayActivity : BaseActivity() {
164166
}
165167

166168
private fun validateInput(text: String): Boolean {
167-
val amount = getAmountFloat(text)
169+
val amount = DonateUtil.getAmountFloat(text)
168170
val min = viewModel.minimumAmount
169171
val max = viewModel.maximumAmount
170172

171173
updateTransactionFee()
172174

173175
if (amount <= 0f || amount < min) {
174-
binding.donateAmountInput.error = getString(R.string.donate_gpay_minimum_amount, viewModel.currencyFormat.format(min))
176+
binding.donateAmountInput.error = getString(R.string.donate_gpay_minimum_amount,
177+
DonateUtil.currencyFormat.format(min))
175178
DonorExperienceEvent.submit("submission_error", "gpay", "error_reason: min_amount")
176179
return false
177180
} else if (max > 0f && amount > max) {
178-
binding.donateAmountInput.error = getString(R.string.donate_gpay_maximum_amount, viewModel.currencyFormat.format(max))
181+
binding.donateAmountInput.error = getString(R.string.donate_gpay_maximum_amount,
182+
DonateUtil.currencyFormat.format(max))
179183
DonorExperienceEvent.submit("submission_error", "gpay", "error_reason: max_amount")
180184
return false
181185
} else {
@@ -217,23 +221,38 @@ class GooglePayActivity : BaseActivity() {
217221
.build())
218222

219223
val viewIds = mutableListOf<Int>()
220-
val presets = donationConfig.currencyAmountPresets[viewModel.currencyCode]
221-
presets?.forEach { amount ->
224+
val presets = donationConfig.currencyAmountPresets[DonateUtil.currencyCode]?.toMutableSet()
225+
if (viewModel.filledAmount > 0f) {
226+
presets?.add(viewModel.filledAmount)
227+
}
228+
var filledAmountButton: MaterialButton? = null
229+
presets?.sorted()?.forEach { amount ->
222230
val viewId = View.generateViewId()
223231
viewIds.add(viewId)
224232
val button = MaterialButton(this)
225-
button.text = viewModel.currencyFormat.format(amount)
233+
button.text = DonateUtil.currencyFormat.format(amount)
226234
button.id = viewId
227235
button.tag = amount
236+
if (amount == viewModel.filledAmount) {
237+
filledAmountButton = button
238+
}
228239
binding.amountPresetsContainer.addView(button)
240+
229241
button.setOnClickListener {
230242
setButtonHighlighted(it)
231243
setAmountText(it.tag as Float)
232244
DonorExperienceEvent.logAction("amount_selected", "gpay")
233245
}
234246
}
235247
binding.amountPresetsFlow.referencedIds = viewIds.toIntArray()
236-
setButtonHighlighted()
248+
setFilledAmountToText()
249+
setButtonHighlighted(filledAmountButton)
250+
}
251+
252+
private fun setFilledAmountToText() {
253+
if (viewModel.filledAmount > 0f) {
254+
setAmountText(viewModel.filledAmount)
255+
}
237256
}
238257

239258
private fun setButtonHighlighted(button: View? = null) {
@@ -252,17 +271,7 @@ class GooglePayActivity : BaseActivity() {
252271

253272
private fun updateTransactionFee() {
254273
binding.checkBoxTransactionFee.text = getString(R.string.donate_gpay_check_transaction_fee,
255-
viewModel.currencyFormat.format(transactionFee))
256-
}
257-
258-
private fun getAmountFloat(text: String): Float {
259-
var result: Float?
260-
result = text.toFloatOrNull()
261-
if (result == null) {
262-
val text2 = if (text.contains(".")) text.replace(".", ",") else text.replace(",", ".")
263-
result = text2.toFloatOrNull()
264-
}
265-
return result ?: 0f
274+
DonateUtil.currencyFormat.format(transactionFee))
266275
}
267276

268277
private fun setAmountText(amount: Float) {
@@ -304,11 +313,13 @@ class GooglePayActivity : BaseActivity() {
304313
companion object {
305314
private const val LOAD_PAYMENT_DATA_REQUEST_CODE = 42
306315
private const val CAMPAIGN_ID_APP_MENU = "appmenu"
316+
const val FILLED_AMOUNT = "filledAmount"
307317

308-
fun newIntent(context: Context, campaignId: String? = null, donateUrl: String? = null): Intent {
318+
fun newIntent(context: Context, campaignId: String? = null, donateUrl: String? = null, filledAmount: Float = 0f): Intent {
309319
return Intent(context, GooglePayActivity::class.java)
310320
.putExtra(DonateDialog.ARG_CAMPAIGN_ID, campaignId)
311321
.putExtra(DonateDialog.ARG_DONATE_URL, donateUrl)
322+
.putExtra(FILLED_AMOUNT, filledAmount)
312323
}
313324
}
314325
}

app/src/extra/java/org/wikipedia/donate/GooglePayComponent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ internal object GooglePayComponent {
7878
return available
7979
}
8080

81-
fun getDonateActivityIntent(activity: Activity, campaignId: String? = null, donateUrl: String? = null): Intent {
82-
return GooglePayActivity.newIntent(activity, campaignId, donateUrl)
81+
fun getDonateActivityIntent(activity: Activity, campaignId: String? = null, donateUrl: String? = null, filledAmount: Float = 0f): Intent {
82+
return GooglePayActivity.newIntent(activity, campaignId, donateUrl, filledAmount)
8383
}
8484

8585
fun getPaymentDataRequestJson(

app/src/extra/java/org/wikipedia/donate/GooglePayViewModel.kt

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.wikipedia.donate
22

3+
import androidx.lifecycle.SavedStateHandle
34
import androidx.lifecycle.ViewModel
45
import androidx.lifecycle.viewModelScope
56
import com.google.android.gms.wallet.PaymentData
@@ -19,52 +20,45 @@ import org.wikipedia.dataclient.donate.CampaignCollection
1920
import org.wikipedia.dataclient.donate.DonationConfig
2021
import org.wikipedia.dataclient.donate.DonationConfigHelper
2122
import org.wikipedia.settings.Prefs
22-
import org.wikipedia.util.GeoUtil
2323
import org.wikipedia.util.Resource
2424
import org.wikipedia.util.log.L
25-
import java.text.NumberFormat
2625
import java.time.Instant
27-
import java.util.Locale
2826
import java.util.concurrent.TimeUnit
2927
import kotlin.math.abs
3028

31-
class GooglePayViewModel : ViewModel() {
29+
class GooglePayViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
30+
val filledAmount = savedStateHandle.get<Float>(GooglePayActivity.FILLED_AMOUNT) ?: 0f
3231
val uiState = MutableStateFlow(Resource<DonationConfig>())
3332
private var donationConfig: DonationConfig? = null
34-
private val currentCountryCode get() = GeoUtil.geoIPCountry.orEmpty()
3533

36-
val currencyFormat: NumberFormat = NumberFormat.getCurrencyInstance(Locale.Builder()
37-
.setLocale(Locale.getDefault()).setRegion(currentCountryCode).build())
38-
val currencyCode get() = currencyFormat.currency?.currencyCode ?: GooglePayComponent.CURRENCY_FALLBACK
39-
val currencySymbol get() = currencyFormat.currency?.symbol ?: "$"
40-
val decimalFormat = GooglePayComponent.getDecimalFormat(currencyCode)
34+
val decimalFormat = GooglePayComponent.getDecimalFormat(DonateUtil.currencyCode)
4135

42-
val transactionFee get() = donationConfig?.currencyTransactionFees?.get(currencyCode)
36+
val transactionFee get() = donationConfig?.currencyTransactionFees?.get(DonateUtil.currencyCode)
4337
?: donationConfig?.currencyTransactionFees?.get("default") ?: 0f
4438

45-
val minimumAmount get() = donationConfig?.currencyMinimumDonation?.get(currencyCode) ?: 0f
39+
val minimumAmount get() = donationConfig?.currencyMinimumDonation?.get(DonateUtil.currencyCode) ?: 0f
4640

4741
val maximumAmount: Float get() {
48-
var max = donationConfig?.currencyMaximumDonation?.get(currencyCode) ?: 0f
42+
var max = donationConfig?.currencyMaximumDonation?.get(DonateUtil.currencyCode) ?: 0f
4943
if (max == 0f) {
5044
val defaultMin = donationConfig?.currencyMinimumDonation?.get(GooglePayComponent.CURRENCY_FALLBACK) ?: 0f
5145
if (defaultMin > 0f) {
52-
max = (donationConfig?.currencyMinimumDonation?.get(currencyCode) ?: 0f) / defaultMin *
46+
max = (donationConfig?.currencyMinimumDonation?.get(DonateUtil.currencyCode) ?: 0f) / defaultMin *
5347
(donationConfig?.currencyMaximumDonation?.get(GooglePayComponent.CURRENCY_FALLBACK) ?: 0f)
5448
}
5549
}
5650
return max
5751
}
5852

59-
val emailOptInRequired get() = donationConfig?.countryCodeEmailOptInRequired.orEmpty().contains(currentCountryCode)
53+
val emailOptInRequired get() = donationConfig?.countryCodeEmailOptInRequired.orEmpty().contains(DonateUtil.currentCountryCode)
6054

6155
var disclaimerInformationSharing: String? = null
6256
var disclaimerMonthlyCancel: String? = null
6357

6458
var finalAmount = 0f
6559

6660
init {
67-
currencyFormat.minimumFractionDigits = 0
61+
DonateUtil.currencyFormat.minimumFractionDigits = 0
6862
load()
6963
}
7064

@@ -75,8 +69,7 @@ class GooglePayViewModel : ViewModel() {
7569
uiState.value = Resource.Loading()
7670

7771
val donationConfigCall = async { DonationConfigHelper.getConfig() }
78-
val donationMessagesCall = async { ServiceFactory.get(WikipediaApp.instance.wikiSite,
79-
DonationConfigHelper.DONATE_WIKI_URL, Service::class.java).getMessages(
72+
val donationMessagesCall = async { ServiceFactory[WikipediaApp.instance.wikiSite, DonationConfigHelper.DONATE_WIKI_URL, Service::class.java].getMessages(
8073
listOf(MSG_DISCLAIMER_INFORMATION_SHARING, MSG_DISCLAIMER_MONTHLY_CANCEL).joinToString("|"),
8174
null, WikipediaApp.instance.appOrSystemLanguageCode) }
8275

@@ -94,7 +87,7 @@ class GooglePayViewModel : ViewModel() {
9487

9588
val paymentMethodsCall = async {
9689
ServiceFactory.get(WikiSite(GooglePayComponent.PAYMENTS_API_URL))
97-
.getPaymentMethods(currentCountryCode)
90+
.getPaymentMethods(DonateUtil.currentCountryCode)
9891
}
9992
paymentMethodsCall.await().response?.let { response ->
10093
Prefs.paymentMethodsLastQueryTime = now
@@ -107,8 +100,8 @@ class GooglePayViewModel : ViewModel() {
107100

108101
if (Prefs.paymentMethodsMerchantId.isEmpty() ||
109102
Prefs.paymentMethodsGatewayId.isEmpty() ||
110-
!donationConfig!!.countryCodeGooglePayEnabled.contains(currentCountryCode) ||
111-
!donationConfig!!.currencyAmountPresets.containsKey(currencyCode)) {
103+
!donationConfig!!.countryCodeGooglePayEnabled.contains(DonateUtil.currentCountryCode) ||
104+
!donationConfig!!.currencyAmountPresets.containsKey(DonateUtil.currencyCode)) {
112105
uiState.value = NoPaymentMethod()
113106
} else {
114107
uiState.value = Resource.Success(donationConfig!!)
@@ -118,7 +111,7 @@ class GooglePayViewModel : ViewModel() {
118111

119112
fun getPaymentDataRequest(): PaymentDataRequest {
120113
return PaymentDataRequest.fromJson(GooglePayComponent.getPaymentDataRequestJson(finalAmount,
121-
currencyCode,
114+
DonateUtil.currencyCode,
122115
Prefs.paymentMethodsMerchantId,
123116
Prefs.paymentMethodsGatewayId
124117
).toString())
@@ -149,17 +142,17 @@ class GooglePayViewModel : ViewModel() {
149142

150143
// The backend expects the final amount in the canonical decimal format, instead of
151144
// any localized format, e.g. comma as decimal separator.
152-
val decimalFormatCanonical = GooglePayComponent.getDecimalFormat(currencyCode, true)
145+
val decimalFormatCanonical = GooglePayComponent.getDecimalFormat(DonateUtil.currencyCode, true)
153146

154147
val response = ServiceFactory.get(WikiSite(GooglePayComponent.PAYMENTS_API_URL))
155148
.submitPayment(
156149
decimalFormatCanonical.format(finalAmount),
157150
BuildConfig.VERSION_NAME,
158151
CampaignCollection.getFormattedCampaignId(campaignId),
159152
billingObj.optString("locality", ""),
160-
currentCountryCode,
161-
currencyCode,
162-
billingObj.optString("countryCode", currentCountryCode),
153+
DonateUtil.currentCountryCode,
154+
DonateUtil.currencyCode,
155+
billingObj.optString("countryCode", DonateUtil.currentCountryCode),
163156
paymentDataObj.optString("email", ""),
164157
billingObj.optString("name", ""),
165158
WikipediaApp.instance.appOrSystemLanguageCode,

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,10 @@
376376
android:name=".readinglist.recommended.RecommendedReadingListSettingsActivity"
377377
android:windowSoftInputMode="adjustResize" />
378378

379+
<activity
380+
android:name=".donate.donationreminder.DonationReminderActivity"
381+
android:windowSoftInputMode="adjustResize"/>
382+
379383
<provider
380384
android:name=".WikipediaFileProvider"
381385
android:authorities="${applicationId}.fileprovider"

0 commit comments

Comments
 (0)