Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patron - Add icons #953

Merged
merged 11 commits into from
May 12, 2023
48 changes: 48 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,54 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_14"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_chrome">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_15"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_round">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_16"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_glow">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_17"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_dark">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>

<activity android:name=".profile.sonos.SonosAppLinkActivity" android:label="@string/profile_sonos"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ private fun Content(
is OnboardingFlow.PlusFlow -> defaultValue = flow.source
else -> Unit // Not a startDestination, default value should not be set.
}
},
navArgument(OnboardingNavRoute.PlusUpgrade.showPatronOnlyArgumentKey) {
type = NavType.BoolType
when (flow) {
is OnboardingFlow.PlusUpsell -> defaultValue = flow.showPatronOnly
else -> Unit // Not a startDestination, default value should not be set.
}
}
)
) { navBackStackEntry ->
Expand Down Expand Up @@ -258,9 +265,10 @@ private object OnboardingNavRoute {
private const val routeBase = "plus_upgrade"

const val sourceArgumentKey = "source"
const val showPatronOnlyArgumentKey = "show_patron_only"
// The route variable should only be used to navigate to the PlusUpgrade screens
// when they are the startDestination. In all other cases, use the routeWithSource function.
const val route = "$routeBase/{$sourceArgumentKey}"
const val route = "$routeBase/{$sourceArgumentKey}/{$showPatronOnlyArgumentKey}"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm passing showPatronOnly boolean as another argument in the upsell flow so that I can access it in OnboardingUpgradeFeaturesViewModel init and show only the Patron plans if the key is set.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right that we don't actually pass this argument when we navigate, but instead we're just using the default value (and we have some logic to intelligently set the default)? If so, that feels a bit confusing to me.

I've definitely run into this issue of how best to get some data from the compose layer to the view model, and it doesn't seem like there's a clear best approach. I wonder if it would be worth directly passing the flow to the view model here in the composable's lambda parameter with something like this (untested) code:

val viewModel = hiltViewModel<OnboardingUpgradeFeaturesViewModel>()
LaunchedEffect(flow) {
    viewModel.setFlow(flow)
}

What you have is working fine though, so feel free to leave it as-is if you prefer. I'm still not sure how best to handle this tbh.

Copy link
Contributor

@mchowning mchowning May 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this some more, It seems like my suggestion would break if multiple views were using this view model (probably not an issue in this case, but as a general matter could be a problem) since currently it is not possible to scope a view model to a particular composable. 🤔

I'm thinking about this more because I have the same issue, but I can't use navigation arguments for my use case really (well, I could, but it would be pretty hacky and hard to understand). Looks like this might be a possibility as well, but I think it's too complicated for me for now. Just sharing this as additional info, it's probably not really applicable to what you're doing here.

While I'm saving links, here is a relevant discussion issue about not currently being able to use assisted injection with hilt.

Copy link
Contributor Author

@ashiagr ashiagr May 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've definitely run into this issue of how best to get some data from the Compose layer to the view model, and it doesn't seem like there's a clear best approach.

That's true. Passing objects via routes from Compose layer to view model is currently not supported. There are hacks to do so using custom nav type and json string <-> object conversion but passing complex data is considered an anti-pattern. I also considered using assisted variables in HiltViewModel but as you pointed it is currently not supported for Compose.

Am I right that we don't actually pass this argument when we navigate, but instead we're just using the default value (and we have some logic to intelligently set the default)? If so, that feels a bit confusing to me.

I understand what you're saying but I wasn't able to find a simpler solution. The approach I took in this PR follows official guidelines to pass nav args similar to how they are passed in "implicit" deep links. Default values are set as arguments for a start destination, the navigation component parses them based on the deep-link-like route that we provide and allows us to access them using saveStateHandle in the view model.

The Navigation component attempts to parse the placeholder values into appropriate types by matching placeholder names to the defined arguments that are defined for the deep link destination.

It is also explained here. Since I require simple boolean and string sources in the view model, sending them using routes fits my case.

To make it more natural so that the route looks closer to a deep-link-like structure, I made showPatronOnly an optional param and added a comment linking the documentation to help understand it in f6f5a8a

New: "$routeBase/{$sourceArgumentKey}?{$showPatronOnlyArgumentKey}={$showPatronOnlyArgumentKey}"
Vs 
Old: "$routeBase/{$sourceArgumentKey}/{$showPatronOnlyArgumentKey}"

Maybe I should also convert the source string to an optional one. 🤔

thinking about this more because I have the same issue, but I can't use navigation arguments for my use case really (well, I could, but it would be pretty hacky and hard to understand).

The recommended approach to getting complex data in view models is getting them using the data layer like through a repository. Lmk your use case though I'm not sure if there are good options currently.

fun routeWithSource(source: OnboardingUpgradeSource) = "$routeBase/$source"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class OnboardingUpgradeFeaturesViewModel @Inject constructor(
val state: StateFlow<OnboardingUpgradeFeaturesState> = _state

private val source = savedStateHandle.get<OnboardingUpgradeSource>("source")
private val showPatronOnly = savedStateHandle.get<Boolean>("show_patron_only")
Comment on lines 52 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of hardcoding these, I think it would be good to reuse the variables from OnboardingNavRoute. In addition to ensuring they stay consistent, this also makes it easier to use code navigation to see where this argument is being set and used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already tried but there's a cyclic dependency between the account and profile modules 😞


init {
if (BuildConfig.ADD_PATRON_ENABLED) {
Expand Down Expand Up @@ -89,7 +90,7 @@ class OnboardingUpgradeFeaturesViewModel @Inject constructor(
val lastSelectedTier = settings.getLastSelectedSubscriptionTier().takeIf { source in listOf(OnboardingUpgradeSource.LOGIN, OnboardingUpgradeSource.PROFILE) }
val lastSelectedFrequency = settings.getLastSelectedSubscriptionFrequency().takeIf { source in listOf(OnboardingUpgradeSource.LOGIN, OnboardingUpgradeSource.PROFILE) }

val showPatronOnly = source == OnboardingUpgradeSource.ACCOUNT_DETAILS
val showPatronOnly = source == OnboardingUpgradeSource.ACCOUNT_DETAILS || showPatronOnly == true
val updatedSubscriptions =
if (showPatronOnly) {
subscriptions.filter { it.tier == Subscription.SubscriptionTier.PATRON }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import au.com.shiftyjelly.pocketcasts.models.to.SignInState
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionType
import au.com.shiftyjelly.pocketcasts.settings.databinding.AdapterAppearanceAppiconItemBinding
import au.com.shiftyjelly.pocketcasts.ui.extensions.getThemeColor
import au.com.shiftyjelly.pocketcasts.ui.helper.AppIcon
Expand All @@ -18,7 +20,7 @@ import au.com.shiftyjelly.pocketcasts.ui.R as UR

class AppearanceIconSettingsAdapter(
private var mainWidth: Int?,
private var isPlusSignedIn: Boolean,
private var signInState: SignInState?,
private var selectedAppIcon: AppIcon.AppIconType,
private val list: List<AppIcon.AppIconType>,
private val clickListener: (AppIcon.AppIconType, AppIcon.AppIconType, Boolean) -> Unit
Expand Down Expand Up @@ -50,8 +52,8 @@ class AppearanceIconSettingsAdapter(

override fun getItemCount() = list.size

fun updatePlusSignedIn(value: Boolean) {
isPlusSignedIn = value
fun updatePlusSignedIn(signInState: SignInState?) {
this.signInState = signInState
notifyDataSetChanged()
}

Expand Down Expand Up @@ -82,20 +84,25 @@ class AppearanceIconSettingsAdapter(
}

fun bind(appIcon: AppIcon.AppIconType, selected: Boolean) {
val showOption = !appIcon.isPlus || isPlusSignedIn
binding.appIconItem.alpha = if (showOption) 1.0f else 0.65f
val isValidIcon = isValidIcon(appIcon)
binding.appIconItem.alpha = if (isValidIcon) 1.0f else 0.65f

val drawable = AppCompatResources.getDrawable(itemView.context, appIcon.settingsIcon)
binding.imgIcon.setImageDrawable(drawable)
var tickDrawable: Drawable? = null
if (showOption && selected) {
if (isValidIcon && selected) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_circle_tick)
} else if (!showOption) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_plus_bubble)
} else if (!isValidIcon) {
val iconDrawable = when (appIcon.type) {
SubscriptionType.PLUS -> R.drawable.ic_locked_plus
SubscriptionType.PATRON -> R.drawable.ic_locked_patron
SubscriptionType.NONE -> throw IllegalStateException("Unknown type found for AppIcon")
}
tickDrawable = AppCompatResources.getDrawable(itemView.context, iconDrawable)
}
binding.imgTick.setImageDrawable(tickDrawable)
binding.imgTick.contentDescription = binding.imgTick.resources.getString(if (selected) LR.string.on else LR.string.off)
binding.imgLock.isVisible = !showOption
binding.imgLock.isVisible = !isValidIcon
binding.txtTitle.setText(appIcon.labelId)

binding.outlinePanel1.setSelectedWithColors(selected, selectColor, deselectColor, strokeWidth)
Expand All @@ -107,11 +114,16 @@ class AppearanceIconSettingsAdapter(
}
val beforeAppIcon = selectedAppIcon
val afterAppIcon = list[bindingAdapterPosition]
val validAppIcon = !afterAppIcon.isPlus || isPlusSignedIn
val validAppIcon = isValidIcon(afterAppIcon)

selectedAppIcon = afterAppIcon
clickListener(beforeAppIcon, afterAppIcon, validAppIcon)
notifyDataSetChanged()
}

private fun isValidIcon(appIcon: AppIcon.AppIconType) =
(appIcon.type == SubscriptionType.NONE) ||
(appIcon.type == SubscriptionType.PLUS && signInState?.isSignedInAsPlus == true) ||
(appIcon.type == SubscriptionType.PATRON && signInState?.isSignedInAsPatron == true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionType
import au.com.shiftyjelly.pocketcasts.preferences.Settings
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager
import au.com.shiftyjelly.pocketcasts.settings.databinding.FragmentSettingsAppearanceBinding
Expand Down Expand Up @@ -85,7 +86,7 @@ class AppearanceSettingsFragment : BaseFragment() {
binding.themeRecyclerView.setHasFixedSize(true)
scrollToCurrentTheme()

binding.appIconRecyclerView.adapter = AppearanceIconSettingsAdapter(mainWidth, isSignedInAsPlus, state.currentAppIcon, state.iconList) { beforeAppIconType, afterAppIconType, validIcon ->
binding.appIconRecyclerView.adapter = AppearanceIconSettingsAdapter(mainWidth, viewModel.signInState.value, state.currentAppIcon, state.iconList) { beforeAppIconType, afterAppIconType, validIcon ->
if (validIcon) {
viewModel.updateGlobalIcon(afterAppIconType)

Expand All @@ -96,7 +97,7 @@ class AppearanceSettingsFragment : BaseFragment() {
.show()
} else {
viewModel.updateChangeAppIconType(Pair(beforeAppIconType, afterAppIconType))
openOnboardingFlow()
openOnboardingFlow(afterAppIconType.type)
}
}
binding.appIconRecyclerView.setHasFixedSize(true)
Expand Down Expand Up @@ -148,7 +149,7 @@ class AppearanceSettingsFragment : BaseFragment() {
}

(binding.themeRecyclerView.adapter as? AppearanceThemeSettingsAdapter)?.updatePlusSignedIn(signInState.isSignedInAsPlus)
(binding.appIconRecyclerView.adapter as? AppearanceIconSettingsAdapter)?.updatePlusSignedIn(signInState.isSignedInAsPlus)
(binding.appIconRecyclerView.adapter as? AppearanceIconSettingsAdapter)?.updatePlusSignedIn(signInState)
binding.upgradeGroup.isVisible = !signInState.isSignedInAsPlus && !settings.getUpgradeClosedAppearSettings()
}

Expand Down Expand Up @@ -191,8 +192,11 @@ class AppearanceSettingsFragment : BaseFragment() {
viewModel.onShown()
}

private fun openOnboardingFlow() {
OnboardingLauncher.openOnboardingFlow(activity, OnboardingFlow.PlusUpsell(OnboardingUpgradeSource.APPEARANCE))
private fun openOnboardingFlow(type: SubscriptionType? = null) {
val onboardingFlow = type?.takeIf { type == SubscriptionType.PATRON }?.let {
OnboardingFlow.PlusUpsell(source = OnboardingUpgradeSource.APPEARANCE, showPatronOnly = true)
} ?: OnboardingFlow.PlusUpsell(OnboardingUpgradeSource.APPEARANCE)
OnboardingLauncher.openOnboardingFlow(activity, onboardingFlow)
}

private fun scrollToCurrentTheme() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class AppearanceThemeSettingsAdapter(
if (showOption && selected) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_circle_tick)
} else if (!showOption) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_plus_bubble)
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_locked_plus)
}
binding.imgTick.setImageDrawable(tickDrawable)
binding.imgTick.contentDescription = binding.imgTick.resources.getString(if (selected) LR.string.on else LR.string.off)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ sealed class OnboardingFlow(val analyticsValue: String) : Parcelable {
@Parcelize object InitialOnboarding : OnboardingFlow("initial_onboarding")
@Parcelize class PlusAccountUpgrade(override val source: OnboardingUpgradeSource) : PlusFlow, OnboardingFlow("plus_account_upgrade")
@Parcelize object PlusAccountUpgradeNeedsLogin : OnboardingFlow("plus_account_upgrade_needs_login")
@Parcelize class PlusUpsell(override val source: OnboardingUpgradeSource) : PlusFlow, OnboardingFlow("plus_upsell")
@Parcelize class PlusUpsell(
override val source: OnboardingUpgradeSource,
val showPatronOnly: Boolean = false,
) : PlusFlow, OnboardingFlow(if (showPatronOnly) "patron_upsell" else "plus_upsell")
@Parcelize class PatronAccountUpgrade(override val source: OnboardingUpgradeSource) : PlusFlow, OnboardingFlow("patron_account_upgrade")

sealed interface PlusFlow {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.toLiveData
import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsEvent
import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsTrackerWrapper
import au.com.shiftyjelly.pocketcasts.analytics.BuildConfig
import au.com.shiftyjelly.pocketcasts.models.to.SignInState
import au.com.shiftyjelly.pocketcasts.preferences.Settings
import au.com.shiftyjelly.pocketcasts.repositories.podcast.UserEpisodeManager
Expand Down Expand Up @@ -64,11 +65,22 @@ class SettingsAppearanceViewModel @Inject constructor(

fun loadThemesAndIcons() {
createAccountState.postValue(SettingsAppearanceState.ThemesAndIconsLoading)

val appIcons = if (BuildConfig.ADD_PATRON_ENABLED) {
appIcon.allAppIconTypes.toList()
} else {
appIcon.allAppIconTypes.toList().filterNot {
it in listOf(
AppIcon.AppIconType.PATRON_CHROME,
AppIcon.AppIconType.PATRON_ROUND,
AppIcon.AppIconType.PATRON_GLOW,
AppIcon.AppIconType.PATRON_DARK,
)
}
}
createAccountState.postValue(
SettingsAppearanceState.ThemesAndIconsLoaded(
theme.activeTheme, theme.allThemes.toList(),
appIcon.activeAppIcon, appIcon.allAppIconTypes.toList()
appIcon.activeAppIcon, appIcons
)
)
}
Expand All @@ -94,6 +106,10 @@ class SettingsAppearanceViewModel @Inject constructor(
AppIcon.AppIconType.ELECTRIC_PINK -> "electric_pink"
AppIcon.AppIconType.RADIOACTIVE -> "radioactive"
AppIcon.AppIconType.HALLOWEEN -> "halloween"
AppIcon.AppIconType.PATRON_CHROME -> "patron_chrome"
AppIcon.AppIconType.PATRON_ROUND -> "patron_round"
AppIcon.AppIconType.PATRON_GLOW -> "patron_glow"
AppIcon.AppIconType.PATRON_DARK -> "patron_dark"
}
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<vector android:height="24dp" android:viewportHeight="30"
android:viewportWidth="30" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#03A9F4" android:pathData="M15,28.75C7.4061,28.75 1.25,22.5939 1.25,15C1.25,7.4061 7.4061,1.25 15,1.25C22.5939,1.25 28.75,7.4061 28.75,15C28.75,22.5939 22.5939,28.75 15,28.75Z"/>
<path android:fillColor="#ffffff" android:pathData="M19.0458,10.4426C19.4917,9.9156 20.2804,9.8499 20.8074,10.2958C21.3344,10.7417 21.4002,11.5304 20.9542,12.0575L13.2017,21.2195L8.4911,16.5089C8.0029,16.0208 8.0029,15.2293 8.4911,14.7411C8.9793,14.253 9.7707,14.253 10.2589,14.7411L13.0483,17.5305L19.0458,10.4426Z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20,33.75C12.406,33.75 6.25,27.594 6.25,20C6.25,12.406 12.406,6.25 20,6.25C27.594,6.25 33.75,12.406 33.75,20C33.75,27.594 27.594,33.75 20,33.75Z"
android:fillColor="#03A9F4"/>
<path
android:pathData="M24.046,15.443C24.492,14.916 25.28,14.85 25.807,15.296C26.334,15.742 26.4,16.53 25.954,17.058L18.202,26.219L13.491,21.509C13.003,21.021 13.003,20.229 13.491,19.741C13.979,19.253 14.771,19.253 15.259,19.741L18.048,22.531L24.046,15.443Z"
android:fillColor="#ffffff"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20.117,20.117m-13.749,-0.118a13.75,13.75 124.03,1 1,27.499 0.235a13.75,13.75 124.03,1 1,-27.499 -0.235"
android:fillColor="#6046F5"/>
<path
android:pathData="M16.75,18.031V17.5C16.75,15.705 18.205,14.25 20,14.25C21.795,14.25 23.25,15.705 23.25,17.5V18.031C23.681,18.142 24,18.534 24,19V24C24,24.552 23.552,25 23,25H17C16.448,25 16,24.552 16,24V19C16,18.534 16.319,18.142 16.75,18.031ZM18.25,17.5C18.25,16.534 19.034,15.75 20,15.75C20.966,15.75 21.75,16.534 21.75,17.5V18H18.25V17.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20.117,20.117m-13.749,-0.118a13.75,13.75 124.03,1 1,27.499 0.235a13.75,13.75 124.03,1 1,-27.499 -0.235">
<aapt:attr name="android:fillColor">
<gradient
android:startX="8.941"
android:startY="6.271"
android:endX="33.794"
android:endY="8.635"
android:type="linear">
<item android:offset="0" android:color="#FFFED745"/>
<item android:offset="1" android:color="#FFFEB525"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M16.75,18.031V17.5C16.75,15.705 18.205,14.25 20,14.25C21.795,14.25 23.25,15.705 23.25,17.5V18.031C23.681,18.142 24,18.534 24,19V24C24,24.552 23.552,25 23,25H17C16.448,25 16,24.552 16,24V19C16,18.534 16.319,18.142 16.75,18.031ZM18.25,17.5C18.25,16.534 19.034,15.75 20,15.75C20.966,15.75 21.75,16.534 21.75,17.5V18H18.25V17.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/appIconItem"
android:layout_width="124dp"
android:layout_height="wrap_content"
Expand All @@ -28,11 +27,9 @@

<ImageView
android:id="@+id/imgTick"
android:layout_width="27dp"
android:layout_height="27dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="bottom|end"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:srcCompat="@drawable/ic_circle_tick" />

</androidx.cardview.widget.CardView>
Expand Down