Skip to content

Commit bf724fb

Browse files
Merge pull request #4 from afterpay/maintenance/update-from-upstream-3.3.0
maintenance: update from upstream 3.3.0
2 parents 21771ac + c9cb786 commit bf724fb

19 files changed

+441
-38
lines changed

README.md

+61-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Add `afterpay-android-button` to your `build.gradle` dependencies.
3838

3939
```gradle
4040
dependencies {
41-
implementation 'com.afterpay:afterpay-android-button:3.2.0'
41+
implementation 'com.afterpay:afterpay-android-button:3.3.0'
4242
}
4343
```
4444

@@ -325,6 +325,14 @@ The Afterpay badge can be added to your layout and scaled to suit the needs of y
325325
![Black on White badge][badge-black-on-white]
326326
![White on Black badge][badge-white-on-black]
327327

328+
### Lockup
329+
330+
The Afterpay lockup can be added to your layout and scaled to suit the needs of your app. Per branding guidelines it requires a minimum width of `64dp`.
331+
332+
![Black Lockup][lockup-black]
333+
![White Lockup][lockup-white]
334+
![Mint Lockup][lockup-mint]
335+
328336
**Attributes**
329337
```xml
330338
app:afterpayColorScheme="blackOnMint|mintOnBlack|blackOnWhite|whiteOnBlack"
@@ -391,6 +399,20 @@ Setting `introText` is optional, will default to `OR` and must be of type `After
391399
Can be any of `OR`, `OR_TITLE`, `MAKE`, `MAKE_TITLE`, `PAY`, `PAY_TITLE`, `IN`, `IN_TITLE`, `PAY_IN`, `PAY_IN_TITLE` or `EMPTY` (no intro text).
392400
Intro text will be rendered lowercase unless using an option suffixed with `_TITLE` in which case title case will be rendered.
393401

402+
##### Logo Type
403+
Setting `logoType` is optional, will default to `BADGE` and must be of type `AfterpayLogoType`.
404+
405+
Can be either of `BADGE` or `LOCKUP`.
406+
When setting color scheme on logo type of `LOCKUP`, only the foreground color will be applied. (See example)
407+
408+
```kotlin
409+
val afterpayBreakdown = view.findViewById<AfterpayPriceBreakdown>(R.id.afterpayPriceBreakdown)
410+
afterpayBreakdown.logoType = AfterpayLogoType.LOCKUP
411+
afterpayBreakdown.colorScheme = AfterpayColorScheme.MINT_ON_BLACK
412+
```
413+
414+
Given the above, the price breakdown will contain the lockup logo and will be of color mint.
415+
394416
##### Optional Words
395417
Setting `showInterestFreeText` and / or `showWithText` is optional and is of type `Boolean`.
396418

@@ -407,12 +429,14 @@ Given the above, the price breakdown text will be rendered `Make 4 interest-free
407429

408430
##### More Info Options
409431
Setting `moreInfoOptions` is optional and of type `AfterpayMoreInfoOptions`. This class has two constructors.
410-
The first takes a single parameter:
432+
The first constructor takes two parameters:
411433
- `modalId`: a `string` that is the filename of a modal hosted on Afterpay static.
434+
- `modalLinkStyle`: an optional value of type `ModalLinkStyle`. See [Modal Link Style Options](#modal-link-style-options) for more details.
412435

413-
The second takes two parameters:
436+
The second constructor takes three parameters:
414437
- `modalTheme`: an enum of type `AfterpayModalTheme` with the following options: `MINT` (default) and `WHITE`.
415438
- `isCbtEnabled`: a `boolean` to indicate if the modal should show the Cross Border Trade details in the modal
439+
- `modalLinkStyle`: an optional value of type `ModalLinkStyle`. See [Modal Link Style Options](#modal-link-style-options) for more details.
416440

417441
**Note**
418442
Not all combinations of Locales and CBT are available.
@@ -426,6 +450,37 @@ afterpayBreakdown.moreInfoOptions = AfterpayMoreInfoOptions(
426450

427451
Given the above, when clicking the more info "link", the modal that opens will be white in the current locale as set in configuration.
428452

453+
###### Modal Link Style Options
454+
A value that can be set on `moreInfoOptions` when initialised. Setting this is optional and is of type `ModalLinkStyle`.
455+
456+
Available values are `CircledInfoIcon`, `MoreInfoText`, `LearnMoreText`, `CircledQuestionIcon`, `CircledLogo`, `Custom`, `None`.
457+
`CircledInfoIcon` is the default & `None` will remove the link altogether.
458+
459+
When using `Custom` the `setContent` (takes a single parameter of type `SpannableStringBuilder`) method should be called first (see second example below).
460+
461+
```kotlin
462+
val afterpayBreakdown = view.findViewById<AfterpayPriceBreakdown>(R.id.afterpayPriceBreakdown)
463+
afterpayBreakdown.moreInfoOptions = AfterpayMoreInfoOptions(
464+
modalLinkStyle = AfterpayModalLinkStyle.CircledInfoIcon
465+
)
466+
```
467+
468+
Given the above, the price breakdown modal link will be a circle containing a question mark.
469+
470+
```kotlin
471+
val afterpayBreakdown = view.findViewById<AfterpayPriceBreakdown>(R.id.afterpayPriceBreakdown)
472+
val content = SpannableStringBuilder().apply {
473+
append("Click ")
474+
append("Here ", StyleSpan(Typeface.BOLD), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
475+
}
476+
AfterpayModalLinkStyle.Custom.setContent(content)
477+
afterpayBreakdown.moreInfoOptions = AfterpayMoreInfoOptions(
478+
modalLinkStyle = AfterpayModalLinkStyle.Custom
479+
)
480+
```
481+
482+
Given the above, the price breakdown modal link will display "Click Here".
483+
429484
## Security
430485

431486
To limit the possibility of a man-in-the-middle attack during the checkout process, certificate pinning can be configured for the Afterpay portal. Please refer to the Android [Network Security Configuration][network-config] documentation for more information.
@@ -466,6 +521,9 @@ This project is licensed under the terms of the Apache 2.0 license. See the [LIC
466521
[badge-mint-on-black]: images/badge_mint_on_black.png
467522
[badge-black-on-white]: images/badge_black_on_white.png
468523
[badge-white-on-black]: images/badge_white_on_black.png
524+
[lockup-black]: images/lockup_black.png
525+
[lockup-white]: images/lockup_white.png
526+
[lockup-mint]: images/lockup_mint.png
469527
[breakdown-available]: images/price_breakdown_available.png
470528
[breakdown-no-configuration]: images/price_breakdown_no_configuration.png
471529
[breakdown-unavailable-min-max]: images/price_breakdown_unavailable_min_max.png

afterpay/src/main/kotlin/com/afterpay/android/internal/AfterpayInfoSpan.kt

+12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package com.afterpay.android.internal
22

33
import android.content.Intent
4+
import android.text.TextPaint
45
import android.text.style.URLSpan
56
import android.view.View
67

78
internal class AfterpayInfoSpan(url: String) : URLSpan(url) {
9+
private var underlined: Boolean = true
10+
11+
constructor(url: String, underlined: Boolean) : this(url) {
12+
this.underlined = underlined
13+
}
14+
815
override fun onClick(widget: View) {
916
val context = widget.context
1017
val intent = Intent(context, AfterpayInfoActivity::class.java).putInfoUrlExtra(url)
@@ -14,4 +21,9 @@ internal class AfterpayInfoSpan(url: String) : URLSpan(url) {
1421
super.onClick(widget)
1522
}
1623
}
24+
25+
override fun updateDrawState(ds: TextPaint) {
26+
super.updateDrawState(ds)
27+
ds.isUnderlineText = this.underlined
28+
}
1729
}

afterpay/src/main/kotlin/com/afterpay/android/internal/Brand.kt

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ internal enum class Brand(
1919
@StringRes val title: Int,
2020
@StringRes val description: Int,
2121
@DrawableRes val badgeForeground: Int,
22+
@DrawableRes val lockup: Int,
2223
@DrawableRes val payNowButtonForeground: Int,
2324
@DrawableRes val buyNowButtonForeground: Int,
2425
@DrawableRes val checkoutButtonForeground: Int,
@@ -29,6 +30,7 @@ internal enum class Brand(
2930
title = R.string.afterpay_service_name,
3031
description = R.string.afterpay_service_name_description,
3132
badgeForeground = R.drawable.afterpay_badge_fg,
33+
lockup = R.drawable.afterpay_lockup,
3234
payNowButtonForeground = R.drawable.afterpay_button_pay_now_fg,
3335
buyNowButtonForeground = R.drawable.afterpay_button_buy_now_fg,
3436
checkoutButtonForeground = R.drawable.afterpay_button_checkout_fg,
@@ -39,6 +41,7 @@ internal enum class Brand(
3941
title = R.string.clearpay_service_name,
4042
description = R.string.clearpay_service_name_description,
4143
badgeForeground = R.drawable.clearpay_badge_fg,
44+
lockup = R.drawable.clearpay_lockup,
4245
payNowButtonForeground = R.drawable.clearpay_button_pay_now_fg,
4346
buyNowButtonForeground = R.drawable.clearpay_button_buy_now_fg,
4447
checkoutButtonForeground = R.drawable.clearpay_button_checkout_fg,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.afterpay.android.view
2+
3+
import android.content.Context
4+
import android.util.AttributeSet
5+
import android.widget.ImageView.ScaleType.FIT_CENTER
6+
import androidx.appcompat.widget.AppCompatImageView
7+
import androidx.core.content.res.use
8+
import com.afterpay.android.Afterpay
9+
import com.afterpay.android.R
10+
import com.afterpay.android.internal.coloredDrawable
11+
import com.afterpay.android.internal.dp
12+
13+
private const val MIN_WIDTH: Int = 64
14+
15+
class AfterpayLockup @JvmOverloads constructor(
16+
context: Context,
17+
attrs: AttributeSet? = null
18+
) : AppCompatImageView(context, attrs) {
19+
20+
var colorScheme: AfterpayColorScheme = AfterpayColorScheme.DEFAULT
21+
set(value) {
22+
field = value
23+
update()
24+
}
25+
26+
init {
27+
contentDescription = resources.getString(Afterpay.brand.title)
28+
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_YES
29+
isFocusable = true
30+
scaleType = FIT_CENTER
31+
adjustViewBounds = true
32+
minimumWidth = MIN_WIDTH.dp
33+
34+
context.theme.obtainStyledAttributes(attrs, R.styleable.Afterpay, 0, 0).use { attributes ->
35+
colorScheme = AfterpayColorScheme.values()[
36+
attributes.getInteger(
37+
R.styleable.Afterpay_afterpayColorScheme,
38+
AfterpayColorScheme.DEFAULT.ordinal
39+
)
40+
]
41+
}
42+
}
43+
44+
private fun update() {
45+
setImageDrawable(
46+
context.coloredDrawable(
47+
drawableResId = Afterpay.brand.lockup,
48+
colorResId = colorScheme.foregroundColorResId
49+
)
50+
)
51+
52+
invalidate()
53+
requestLayout()
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.afterpay.android.view
2+
3+
enum class AfterpayLogoType(val fontHeightMultiplier: Double) {
4+
BADGE(2.5),
5+
LOCKUP(1.0);
6+
7+
internal companion object {
8+
9+
@JvmField
10+
val DEFAULT = AfterpayLogoType.BADGE
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.afterpay.android.view
2+
3+
import com.afterpay.android.R
4+
5+
sealed class AfterpayModalLinkStyle(val config: ModalLinkConfig) {
6+
constructor(string: String, config: ModalLinkConfig) : this(config)
7+
8+
object CircledInfoIcon : AfterpayModalLinkStyle(ModalLinkConfig(text = R.string.afterpay_price_breakdown_link_circled_info_icon, underlined = false))
9+
object MoreInfoText : AfterpayModalLinkStyle(ModalLinkConfig(text = R.string.afterpay_price_breakdown_link_more_info_text))
10+
object LearnMoreText : AfterpayModalLinkStyle(ModalLinkConfig(text = R.string.afterpay_price_breakdown_link_learn_more_text))
11+
object CircledQuestionIcon : AfterpayModalLinkStyle(
12+
ModalLinkConfig(
13+
image = R.drawable.icon_circled_question,
14+
imageRenderingMode = AfterpayImageRenderingMode.TEMPLATE
15+
)
16+
)
17+
object CircledLogo : AfterpayModalLinkStyle(
18+
ModalLinkConfig(
19+
image = R.drawable.afterpay_logo_small,
20+
imageRenderingMode = AfterpayImageRenderingMode.ORIGINAL
21+
)
22+
)
23+
object Custom : AfterpayModalLinkStyle(ModalLinkConfig()) {
24+
public fun setContent(content: CharSequence) {
25+
config.customContent = content
26+
}
27+
}
28+
object None : AfterpayModalLinkStyle(ModalLinkConfig())
29+
30+
internal companion object {
31+
32+
@JvmField
33+
val DEFAULT = CircledInfoIcon
34+
}
35+
}
36+
37+
internal enum class AfterpayImageRenderingMode {
38+
ORIGINAL,
39+
TEMPLATE
40+
}
41+
42+
internal data class ModalLinkConfig(
43+
val text: Int? = null,
44+
val image: Int? = null,
45+
val imageRenderingMode: AfterpayImageRenderingMode? = null,
46+
var customContent: CharSequence? = null,
47+
val underlined: Boolean = true
48+
)

afterpay/src/main/kotlin/com/afterpay/android/view/AfterpayModalTheme.kt

+6
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,10 @@ package com.afterpay.android.view
33
enum class AfterpayModalTheme(val slug: String) {
44
MINT(""),
55
WHITE("-theme-white");
6+
7+
internal companion object {
8+
9+
@JvmField
10+
val DEFAULT = AfterpayModalTheme.MINT
11+
}
612
}

afterpay/src/main/kotlin/com/afterpay/android/view/AfterpayMoreInfoOptions.kt

+12-5
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@ package com.afterpay.android.view
33
import com.afterpay.android.Afterpay
44

55
class AfterpayMoreInfoOptions {
6-
private var modalId: String? = null
7-
private var modalTheme: AfterpayModalTheme = AfterpayModalTheme.MINT
8-
private var isCbtEnabled: Boolean = false
6+
internal var modalId: String? = null
7+
internal var modalLinkStyle: AfterpayModalLinkStyle = AfterpayModalLinkStyle.DEFAULT
8+
internal var modalTheme: AfterpayModalTheme = AfterpayModalTheme.DEFAULT
9+
internal var isCbtEnabled: Boolean = false
910

1011
/**
1112
* Set up options for the more info link in AfterpayPriceBreakdown
1213
*
1314
* @param modalId the filename of a modal hosted on Afterpay static
1415
*/
15-
constructor(modalId: String) {
16+
constructor(
17+
modalId: String,
18+
modalLinkStyle: AfterpayModalLinkStyle = AfterpayModalLinkStyle.DEFAULT
19+
) {
1620
this.modalId = modalId
21+
this.modalLinkStyle = modalLinkStyle
1722
}
1823

1924
/**
@@ -27,10 +32,12 @@ class AfterpayMoreInfoOptions {
2732
*/
2833
constructor(
2934
modalTheme: AfterpayModalTheme = AfterpayModalTheme.MINT,
30-
isCbtEnabled: Boolean = false
35+
isCbtEnabled: Boolean = false,
36+
modalLinkStyle: AfterpayModalLinkStyle = AfterpayModalLinkStyle.DEFAULT
3137
) {
3238
this.modalTheme = modalTheme
3339
this.isCbtEnabled = isCbtEnabled
40+
this.modalLinkStyle = modalLinkStyle
3441
}
3542

3643
internal fun modalFile(): String {

0 commit comments

Comments
 (0)