From b9d1031eda0b90d0e8a5e2ae7b0c17e67f5735a9 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 21 Jan 2026 15:50:20 +0100 Subject: [PATCH 01/17] feat(account): update strings.xml --- core/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index afa6acbb..dee0a1e2 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -354,7 +354,7 @@ Retry Visit Device Management Enable Device Management - Switch to IVPN Pro + Upgrade your subscription Cancel login Error From ca9a699bab9d92cddaaf557cd2f859cf122db42c Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 22 Jan 2026 14:30:54 +0100 Subject: [PATCH 02/17] feat(account): update Plan.kt --- .../main/java/net/ivpn/core/v2/signup/Plan.kt | 78 ++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt index dc6cdd9c..4bc9355e 100644 --- a/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt +++ b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt @@ -22,19 +22,77 @@ package net.ivpn.core.common.billing.addfunds along with the IVPN Android app. If not, see . */ -enum class Plan(val skuPath: String, val productName: String) { - PRO("net.ivpn.subscriptions.pro.", "IVPN Pro"), - STANDARD("net.ivpn.subscriptions.standard.", "IVPN Standard"); +enum class Plan( + val skuPath: String, + val productName: String, + val title: String, + val description: String +) { + STANDARD( + skuPath = "net.ivpn.subscriptions.standard.", + productName = "IVPN Standard", + title = "IVPN Standard", + description = "IVPN on 5 devices" + ), + PLUS( + skuPath = "net.ivpn.subscriptions.plus.", + productName = "IVPN Plus", + title = "IVPN Plus", + description = "IVPN on 10 devices, modDNS, Mailx" + ), + PRO( + skuPath = "net.ivpn.subscriptions.pro.", + productName = "IVPN Pro", + title = "IVPN Pro Suite", + description = "IVPN on 10 devices, modDNS, Mailx, Portmaster Pro" + ); companion object { + fun getPlanByProductName(productName: String?): Plan { - for (plan in values()) { - if (plan.productName == productName) { - return plan - } - } + return values().firstOrNull { it.productName == productName } ?: STANDARD + } + + fun getPlan(currentPlan: String?): Plan { + if (currentPlan == null) return STANDARD - return STANDARD + return when { + currentPlan.contains("Plus", ignoreCase = true) -> PLUS + currentPlan.contains("Pro", ignoreCase = true) -> PRO + else -> STANDARD + } } } -} \ No newline at end of file + + fun getPlanTitle(): String = title + + fun getPlanDesc(): String = description + + fun getAltTitleOne(): String = + when (this) { + STANDARD -> PLUS.title + PLUS -> STANDARD.title + PRO -> STANDARD.title + } + + fun getAltDescOne(): String = + when (this) { + STANDARD -> PLUS.description + PLUS -> STANDARD.description + PRO -> STANDARD.description + } + + fun getAltTitleTwo(): String = + when (this) { + STANDARD -> PRO.title + PLUS -> PRO.title + PRO -> PLUS.title + } + + fun getAltDescTwo(): String = + when (this) { + STANDARD -> PRO.description + PLUS -> PRO.description + PRO -> PLUS.description + } +} From aba0562c9e47d186a7d92adce48bd93a7b13e066 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 22 Jan 2026 15:21:31 +0100 Subject: [PATCH 03/17] feat(account): update AccountViewModel.kt --- .../java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt | 7 +++++++ core/src/main/res/layout/content_account.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt index 62841f6a..a2e78e5b 100644 --- a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt +++ b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt @@ -28,6 +28,7 @@ import androidx.databinding.ObservableField import androidx.databinding.ObservableLong import androidx.lifecycle.ViewModel import net.ivpn.core.IVPNApplication +import net.ivpn.core.common.billing.addfunds.Plan import net.ivpn.core.common.dagger.ApplicationScope import net.ivpn.core.common.prefs.EncryptedUserPreference import net.ivpn.core.common.qr.QRController @@ -65,6 +66,7 @@ class AccountViewModel @Inject constructor( val isActive = ObservableBoolean() val deviceManagement = ObservableBoolean() val deviceName = ObservableField() + val plan = ObservableField() val isExpired = ObservableBoolean() val isExpiredIn = ObservableBoolean() @@ -105,6 +107,7 @@ class AccountViewModel @Inject constructor( paymentMethod = getPaymentMethodValue() deviceManagement.set(getDeviceManagement()) deviceName.set(getDeviceName()) + plan.set(getPlanByProductName(accountType.get())) updateExpireData() } @@ -281,6 +284,10 @@ class AccountViewModel @Inject constructor( return userPreference.getDeviceName() } + private fun getPlanByProductName(productName: String?): Plan { + return Plan.getPlanByProductName(productName) + } + interface AccountNavigator { fun onLogOut() diff --git a/core/src/main/res/layout/content_account.xml b/core/src/main/res/layout/content_account.xml index c0d78891..f38a07c9 100644 --- a/core/src/main/res/layout/content_account.xml +++ b/core/src/main/res/layout/content_account.xml @@ -137,7 +137,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:alpha="0.7" - android:text="@string/account_type" + android:text="@string/subscription_title" android:textColor="@color/account_text" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/line" /> From 644c69f4c68a105db4968bdd190eff4c568f4108 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 22 Jan 2026 18:00:15 +0100 Subject: [PATCH 04/17] feat(account): update content_account.xml --- core/src/main/res/layout/content_account.xml | 117 ++++++++++--------- core/src/main/res/values/strings.xml | 1 + 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/core/src/main/res/layout/content_account.xml b/core/src/main/res/layout/content_account.xml index f38a07c9..78cefdea 100644 --- a/core/src/main/res/layout/content_account.xml +++ b/core/src/main/res/layout/content_account.xml @@ -1,5 +1,6 @@ - @@ -151,9 +152,23 @@ android:letterSpacing="-0.03" android:text="@{account.accountType}" android:textColor="@color/account_text" - android:textSize="16sp" + android:textSize="18sp" + android:fontFamily="sans-serif-medium" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/account_type_label" + app:layout_constraintBottom_toTopOf="@+id/plan_desc"/> + + + app:layout_constraintTop_toBottomOf="@+id/account_label" /> + app:layout_constraintTop_toBottomOf="@+id/plan_desc"> - - - - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@+id/textView32"/> + android:textSize="18sp" + android:fontFamily="sans-serif-medium" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView31" + app:layout_constraintBottom_toTopOf="@+id/textView33"/> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView32" + app:layout_constraintBottom_toTopOf="@+id/textView34"/> + + + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView34" + app:layout_constraintBottom_toBottomOf="parent"/> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index dee0a1e2..5daaa8aa 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -638,6 +638,7 @@ Please save your account ID in a safe place, you will use it to connect on another IVPN App or log in to the client area of the IVPN website. You can find this account ID later in the IVPN App Account screen. Select your plan to continue Account ID copied + Available on IVPN website: Device Name Your current location From fc23502d1333d986126a05aef7c3b8532ab8abe6 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 23 Jan 2026 12:05:16 +0100 Subject: [PATCH 05/17] feat(account): update SignUpAccountCreatedFragment.kt --- core/src/main/res/values/strings.xml | 2 +- .../net/ivpn/client/signup/SignUpAccountCreatedFragment.kt | 4 +++- store/src/main/res/navigation/store_nav_graph.xml | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 5daaa8aa..79a7d2ae 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -636,7 +636,7 @@ Active until Log out Please save your account ID in a safe place, you will use it to connect on another IVPN App or log in to the client area of the IVPN website. You can find this account ID later in the IVPN App Account screen. - Select your plan to continue + Continue to payment Account ID copied Available on IVPN website: Device Name diff --git a/store/src/main/java/net/ivpn/client/signup/SignUpAccountCreatedFragment.kt b/store/src/main/java/net/ivpn/client/signup/SignUpAccountCreatedFragment.kt index 360b97e0..450918b6 100644 --- a/store/src/main/java/net/ivpn/client/signup/SignUpAccountCreatedFragment.kt +++ b/store/src/main/java/net/ivpn/client/signup/SignUpAccountCreatedFragment.kt @@ -38,6 +38,7 @@ import androidx.navigation.ui.setupWithNavController import net.ivpn.client.R import net.ivpn.client.StoreIVPNApplication import net.ivpn.client.databinding.FragmentSignUpFinishBinding +import net.ivpn.core.common.billing.addfunds.Plan import net.ivpn.core.common.utils.ToastUtil import net.ivpn.core.v2.MainActivity import org.slf4j.LoggerFactory @@ -110,7 +111,8 @@ class SignUpAccountCreatedFragment : Fragment() { } private fun continuePurchase() { - val action = SignUpAccountCreatedFragmentDirections.actionSignUpAccountCreatedFragmentToSignUpProductFragment() + viewModel.selectedPlan.set(Plan.STANDARD) + val action = SignUpAccountCreatedFragmentDirections.actionSignUpAccountCreatedFragmentToSignUpPeriodFragment2() NavHostFragment.findNavController(this).navigate(action) } } \ No newline at end of file diff --git a/store/src/main/res/navigation/store_nav_graph.xml b/store/src/main/res/navigation/store_nav_graph.xml index 19446e9a..08422f75 100644 --- a/store/src/main/res/navigation/store_nav_graph.xml +++ b/store/src/main/res/navigation/store_nav_graph.xml @@ -11,6 +11,9 @@ + Date: Fri, 23 Jan 2026 12:20:13 +0100 Subject: [PATCH 06/17] feat(account): update content_sign_up_period.xml --- .../main/java/net/ivpn/core/common/utils/StringUtil.java | 6 ------ store/src/main/res/layout/content_sign_up_period.xml | 7 +++---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/common/utils/StringUtil.java b/core/src/main/java/net/ivpn/core/common/utils/StringUtil.java index 94803d7d..2581e9ec 100644 --- a/core/src/main/java/net/ivpn/core/common/utils/StringUtil.java +++ b/core/src/main/java/net/ivpn/core/common/utils/StringUtil.java @@ -81,10 +81,4 @@ public static String formatTimeUntilResumed(long timeUntilResumed) { return builder.toString(); } -// public static String formatPlanName(Plan plan) { -// if (plan == null) { -// return ""; -// } -// return "IVPN " + plan.toString(); -// } } \ No newline at end of file diff --git a/store/src/main/res/layout/content_sign_up_period.xml b/store/src/main/res/layout/content_sign_up_period.xml index 1b752153..ef7f7246 100644 --- a/store/src/main/res/layout/content_sign_up_period.xml +++ b/store/src/main/res/layout/content_sign_up_period.xml @@ -90,10 +90,9 @@ android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:letterSpacing="0.04" - android:text="@{Utils.formatPlanName(viewmodel.selectedPlan)}" - android:textAllCaps="true" + android:text="@{viewmodel.selectedPlan.getPlanTitle()}" android:textColor="@color/sign_up_text_color" - android:textSize="16sp" + android:textSize="18sp" android:textStyle="bold" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/divider_1" /> @@ -108,7 +107,7 @@ android:text="@string/sign_up_period_change" android:textColor="@color/primary" android:textSize="16sp" - android:visibility="@{viewmodel.blankAccountID != null ? View.VISIBLE : View.GONE}" + android:visibility="@{View.GONE}" app:layout_constraintBottom_toBottomOf="@+id/standard_plan_title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/standard_plan_title" /> From 1e0f260938fc1047d4ff20c50e4702fd55ab44a1 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 23 Jan 2026 12:30:45 +0100 Subject: [PATCH 07/17] feat(account): update EncryptedUserPreference.kt --- .../java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt b/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt index 837c72bc..15f39dfe 100644 --- a/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt +++ b/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt @@ -130,7 +130,8 @@ class EncryptedUserPreference @Inject constructor(val preference: Preference) { } fun getCapabilityMultiHop(): Boolean { - return sharedPreferences.getBoolean(USER_MULTI_HOP, false) + // return sharedPreferences.getBoolean(USER_MULTI_HOP, false) + return true } fun getPaymentMethod(): String { From cf9d7f4d2029d94de9c2c6a692e739a4b94f1c57 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 23 Jan 2026 14:22:28 +0100 Subject: [PATCH 08/17] feat(account): update ConsumableProducts.kt --- .../ivpn/client/billing/ConsumableProducts.kt | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/store/src/main/java/net/ivpn/client/billing/ConsumableProducts.kt b/store/src/main/java/net/ivpn/client/billing/ConsumableProducts.kt index 2a46e634..462de17e 100644 --- a/store/src/main/java/net/ivpn/client/billing/ConsumableProducts.kt +++ b/store/src/main/java/net/ivpn/client/billing/ConsumableProducts.kt @@ -25,37 +25,19 @@ package net.ivpn.core.common.billing object ConsumableProducts { - const val ONE_WEEK_STANDARD = "net.ivpn.subscriptions.standard.1week"; - const val ONE_WEEK_PRO = "net.ivpn.subscriptions.pro.1week"; - - const val ONE_MONTH_STANDARD = "net.ivpn.subscriptions.standard.1month"; - const val ONE_MONTH_PRO = "net.ivpn.subscriptions.pro.1month"; - + const val ONE_WEEK_STANDARD = "net.ivpn.subscriptions.standard.1week" + const val ONE_MONTH_STANDARD = "net.ivpn.subscriptions.standard.1month" const val ONE_YEAR_STANDARD = "net.ivpn.subscriptions.standard.1year" - const val ONE_YEAR_PRO = "net.ivpn.subscriptions.pro.1year" - const val TWO_YEARS_STANDARD = "net.ivpn.subscriptions.standard.2year" - const val TWO_YEARS_PRO = "net.ivpn.subscriptions.pro.2year" - const val THREE_YEARS_STANDARD = "net.ivpn.subscriptions.standard.3year" - const val THREE_YEARS_PRO = "net.ivpn.subscriptions.pro.3year" fun getConsumableSKUs(): List { val skuList = mutableListOf() skuList.add(ONE_WEEK_STANDARD) - skuList.add(ONE_WEEK_PRO) - skuList.add(ONE_MONTH_STANDARD) - skuList.add(ONE_MONTH_PRO) - skuList.add(ONE_YEAR_STANDARD) - skuList.add(ONE_YEAR_PRO) - skuList.add(TWO_YEARS_STANDARD) - skuList.add(TWO_YEARS_PRO) - skuList.add(THREE_YEARS_STANDARD) - skuList.add(THREE_YEARS_PRO) return skuList } From 3307d5cb9c39a1427c7d6203ce4cfe1e81cea701 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Tue, 27 Jan 2026 14:46:03 +0100 Subject: [PATCH 09/17] feat(account): update Plan.kt --- .../connect/createSession/CreateSessionFragment.java | 4 ++-- core/src/main/java/net/ivpn/core/v2/signup/Plan.kt | 12 +++++------- core/src/main/res/layout/content_account.xml | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java b/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java index d1b452a7..ebfd395b 100644 --- a/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java +++ b/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java @@ -91,12 +91,12 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c } // Device Management enabled, Standard plan - if (deviceManagement && plan.equals(Plan.STANDARD) && isAccountNewStyle) { + if (deviceManagement && !plan.equals(Plan.PRO) && isAccountNewStyle) { return getDmStandardBinding(inflater, container); } // Device Management disabled, Standard plan - if (!deviceManagement && plan.equals(Plan.STANDARD) && isAccountNewStyle) { + if (!deviceManagement && !plan.equals(Plan.PRO) && isAccountNewStyle) { return getStandardBinding(inflater, container); } diff --git a/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt index 4bc9355e..99609aab 100644 --- a/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt +++ b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt @@ -50,15 +50,11 @@ enum class Plan( companion object { fun getPlanByProductName(productName: String?): Plan { - return values().firstOrNull { it.productName == productName } ?: STANDARD - } - - fun getPlan(currentPlan: String?): Plan { - if (currentPlan == null) return STANDARD + if (productName == null) return STANDARD return when { - currentPlan.contains("Plus", ignoreCase = true) -> PLUS - currentPlan.contains("Pro", ignoreCase = true) -> PRO + productName.contains("Plus", ignoreCase = true) -> PLUS + productName.contains("Pro", ignoreCase = true) -> PRO else -> STANDARD } } @@ -68,6 +64,8 @@ enum class Plan( fun getPlanDesc(): String = description + fun isStandard(): Boolean = this == STANDARD + fun getAltTitleOne(): String = when (this) { STANDARD -> PLUS.title diff --git a/core/src/main/res/layout/content_account.xml b/core/src/main/res/layout/content_account.xml index 78cefdea..8d95b60a 100644 --- a/core/src/main/res/layout/content_account.xml +++ b/core/src/main/res/layout/content_account.xml @@ -150,7 +150,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="4dp" android:letterSpacing="-0.03" - android:text="@{account.accountType}" + android:text="@{account.plan.getPlanTitle()}" android:textColor="@color/account_text" android:textSize="18sp" android:fontFamily="sans-serif-medium" @@ -247,7 +247,7 @@ android:text="@string/account_add_more_time" android:textColor="@color/primary" android:textSize="15sp" - android:visibility="@{account.accountLegacy ? View.GONE : View.VISIBLE}" + android:visibility="@{account.plan.isStandard() ? View.VISIBLE : View.GONE}" app:layout_constraintBottom_toBottomOf="@+id/active_until" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/active_until_label" /> From fa26f9129403c1ae38c6df4c21c3d36ca3525495 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Wed, 28 Jan 2026 16:46:06 +0100 Subject: [PATCH 10/17] feat(account): update SignUpViewModel.kt --- .../src/main/java/net/ivpn/core/v2/dialog/Dialogs.java | 4 ++-- core/src/main/res/layout/fragment_connect.xml | 2 ++ core/src/main/res/values/strings.xml | 3 +++ .../java/net/ivpn/client/signup/SignUpViewModel.kt | 10 +++++++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/v2/dialog/Dialogs.java b/core/src/main/java/net/ivpn/core/v2/dialog/Dialogs.java index 93ccd3e9..428fc0d7 100644 --- a/core/src/main/java/net/ivpn/core/v2/dialog/Dialogs.java +++ b/core/src/main/java/net/ivpn/core/v2/dialog/Dialogs.java @@ -80,8 +80,8 @@ public enum Dialogs { REMOVE_KILL_SWITCH(R.string.dialogs_please_note, R.string.dialogs_remove_kill_switch, R.string.dialogs_to_read_more, R.string.dialogs_ok), WG_CANT_CHANGE_PORT(R.string.dialogs_please_note, R.string.dialogs_wireguard_impossible_change_port, -1, R.string.dialogs_ok), WG_QUANTUM_RESISTANCE_INFO(R.string.protocol_wg_quantum_resistance, R.string.protocol_wg_quantum_resistance_info, -1, R.string.dialogs_ok), - DEVICE_LOGGED_OUT(R.string.dialogs_device_logged_out_title, R.string.dialogs_device_logged_out_message, -1, R.string.dialogs_ok); - + DEVICE_LOGGED_OUT(R.string.dialogs_device_logged_out_title, R.string.dialogs_device_logged_out_message, -1, R.string.dialogs_ok), + ACCOUNT_INACTIVE(R.string.dialogs_account_expired_title, R.string.dialogs_account_expired_msg, -1, R.string.dialogs_ok); private int titleId; private int messageId; private int positiveBtnId; diff --git a/core/src/main/res/layout/fragment_connect.xml b/core/src/main/res/layout/fragment_connect.xml index 52efc9b3..3dba2067 100644 --- a/core/src/main/res/layout/fragment_connect.xml +++ b/core/src/main/res/layout/fragment_connect.xml @@ -161,6 +161,7 @@ android:letterSpacing="-0.03" android:paddingHorizontal="16dp" android:text="Renew" + android:visibility="@{account.plan.isStandard() ? View.VISIBLE : View.GONE}" android:textAllCaps="true" android:textColor="@color/alerts_text" android:textSize="12sp" /> @@ -202,6 +203,7 @@ android:letterSpacing="-0.03" android:paddingHorizontal="16dp" android:text="Renew" + android:visibility="@{account.plan.isStandard() ? View.VISIBLE : View.GONE}" android:textAllCaps="true" android:textColor="@color/alerts_text" android:textSize="12sp" /> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 79a7d2ae..33204638 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -479,6 +479,9 @@ You are logged out You have been redirected to the login page to re-enter your credentials. + Your account is expired + Please renew your account to use this feature. + Are you sure? This will disconnect you from IVPN. Log out and clear app settings Log out diff --git a/store/src/main/java/net/ivpn/client/signup/SignUpViewModel.kt b/store/src/main/java/net/ivpn/client/signup/SignUpViewModel.kt index e7cf30da..210eca3b 100644 --- a/store/src/main/java/net/ivpn/client/signup/SignUpViewModel.kt +++ b/store/src/main/java/net/ivpn/client/signup/SignUpViewModel.kt @@ -23,8 +23,10 @@ package net.ivpn.client.signup */ import android.app.Activity +import android.content.Context import android.content.Intent import android.net.Uri +import androidx.core.content.ContentProviderCompat.requireContext import androidx.databinding.ObservableField import androidx.navigation.NavController import com.android.billingclient.api.BillingClient @@ -50,6 +52,8 @@ import net.ivpn.core.rest.data.addfunds.NewAccountRequestBody import net.ivpn.core.rest.data.addfunds.NewAccountResponse import net.ivpn.core.rest.requests.common.Request import net.ivpn.core.rest.requests.common.RequestWrapper +import net.ivpn.core.v2.dialog.DialogBuilder +import net.ivpn.core.v2.dialog.Dialogs import net.ivpn.core.v2.signup.SignUpController import org.slf4j.LoggerFactory import java.util.Calendar @@ -153,6 +157,11 @@ class SignUpViewModel @Inject constructor( override fun signUpWithInactiveAccount(navController: NavController?, plan: Plan, isAccountNewStyle: Boolean) { if (isAccountNewStyle) { + if (!plan.isStandard()) { + DialogBuilder.createNotificationDialog(navController?.context, Dialogs.ACCOUNT_INACTIVE) + return + } + blankAccountID.set(null) selectedPlan.set(plan) @@ -163,7 +172,6 @@ class SignUpViewModel @Inject constructor( } } - override fun reset() { dataLoading.set(false) selectedPeriod.set(null) From a3a638dee944975388ea1cb74ddd30d649ec3580 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 29 Jan 2026 12:32:16 +0100 Subject: [PATCH 11/17] feat(account): update content_account.xml --- .../main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt | 4 ++++ core/src/main/res/layout/content_account.xml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt index a2e78e5b..595e34a4 100644 --- a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt +++ b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt @@ -192,6 +192,10 @@ class AccountViewModel @Inject constructor( return isNewStyleAccount(username.get().toString()) } + fun showAddMoreTime(): Boolean { + return isAccountStandard() && isAccountNewStyle() + } + private fun clearLocalCache() { authenticated.set(false) } diff --git a/core/src/main/res/layout/content_account.xml b/core/src/main/res/layout/content_account.xml index 8d95b60a..d15cfd74 100644 --- a/core/src/main/res/layout/content_account.xml +++ b/core/src/main/res/layout/content_account.xml @@ -247,7 +247,7 @@ android:text="@string/account_add_more_time" android:textColor="@color/primary" android:textSize="15sp" - android:visibility="@{account.plan.isStandard() ? View.VISIBLE : View.GONE}" + android:visibility="@{account.showAddMoreTime() ? View.VISIBLE : View.GONE}" app:layout_constraintBottom_toBottomOf="@+id/active_until" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/active_until_label" /> @@ -278,6 +278,7 @@ android:layout_marginEnd="16dp" android:background="@drawable/plan_details_background" android:padding="14dp" + android:visibility="@{account.isAccountNewStyle() ? View.VISIBLE : View.GONE}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/plan_desc"> From f7da7262d84d061cf784552204b5b5492c8609a5 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 29 Jan 2026 18:04:42 +0100 Subject: [PATCH 12/17] feat(account): update content_account.xml --- .../core/v2/viewmodel/AccountViewModel.kt | 7 ++++- core/src/main/res/layout/content_account.xml | 28 ++++++++++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt index 595e34a4..b3a628f1 100644 --- a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt +++ b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt @@ -185,7 +185,12 @@ class AccountViewModel @Inject constructor( } fun isAccountLegacy(): Boolean { - return accountType.get()?.equals("Member VPN Pro Account") ?: false + return username.get()?.startsWith("ivpn") ?: false + } + + fun isAccountLegacyTeam(): Boolean { + val user = username.get() ?: return false + return user.startsWith("ivpn") && accountType.get()?.contains("Member") == true } fun isAccountNewStyle(): Boolean { diff --git a/core/src/main/res/layout/content_account.xml b/core/src/main/res/layout/content_account.xml index d15cfd74..2b080c55 100644 --- a/core/src/main/res/layout/content_account.xml +++ b/core/src/main/res/layout/content_account.xml @@ -150,14 +150,32 @@ android:layout_marginStart="16dp" android:layout_marginTop="4dp" android:letterSpacing="-0.03" - android:text="@{account.plan.getPlanTitle()}" + android:text="@{account.accountType}" android:textColor="@color/account_text" android:textSize="18sp" android:fontFamily="sans-serif-medium" + android:visibility="@{account.isAccountNewStyle() ? View.VISIBLE : View.GONE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/account_type_label" app:layout_constraintBottom_toTopOf="@+id/plan_desc"/> + + @@ -176,6 +195,7 @@ android:layout_height="1dp" android:layout_marginTop="16dp" android:background="@color/account_line" + android:visibility="@{account.isAccountLegacyTeam() ? View.GONE : View.VISIBLE}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/plan_details" /> @@ -189,7 +209,7 @@ android:alpha="0.7" android:text="@string/account_active_until" android:textColor="@color/account_text" - android:visibility="@{account.accountLegacy ? View.GONE : View.VISIBLE}" + android:visibility="@{account.isAccountLegacyTeam() ? View.GONE : View.VISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/line2" /> @@ -203,7 +223,7 @@ android:text="@{account.isActive() ? DateUtil.formatDate(account.availableUntil) : @string/account_no_active_subscription}" android:textColor="@color/account_text" android:textSize="16sp" - android:visibility="@{account.accountLegacy ? View.GONE : View.VISIBLE}" + android:visibility="@{account.isAccountLegacyTeam() ? View.GONE : View.VISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/active_until_label" /> @@ -213,7 +233,7 @@ android:layout_height="1dp" android:layout_marginTop="24dp" android:background="@color/account_line" - android:visibility="@{account.accountLegacy ? View.GONE : View.VISIBLE}" + android:visibility="@{account.isAccountLegacyTeam() ? View.GONE : View.VISIBLE}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/active_until" /> From 638964983010dd3eb8cb3b7121c2d116066c7a9d Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 29 Jan 2026 18:07:13 +0100 Subject: [PATCH 13/17] feat(account): update bottom_sheet_legacy_standard.xml --- .../layout/bottom_sheet_legacy_standard.xml | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/core/src/main/res/layout/bottom_sheet_legacy_standard.xml b/core/src/main/res/layout/bottom_sheet_legacy_standard.xml index 7bad55d9..3ff6139e 100644 --- a/core/src/main/res/layout/bottom_sheet_legacy_standard.xml +++ b/core/src/main/res/layout/bottom_sheet_legacy_standard.xml @@ -91,34 +91,6 @@ - - - - - - - - Date: Thu, 29 Jan 2026 18:09:34 +0100 Subject: [PATCH 14/17] feat(account): update CreateSessionFragment.java --- .../core/v2/connect/createSession/CreateSessionFragment.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java b/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java index ebfd395b..130b9b94 100644 --- a/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java +++ b/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java @@ -159,11 +159,6 @@ private View getLegacyStandardBinding(@NonNull LayoutInflater inflater, @Nullabl navigator.tryAgain(); } }); - binding.upgradePlan.setOnClickListener(view -> { - if (navigator != null) { - navigator.upgradePlan(upgradeToUrl); - } - }); binding.close.setOnClickListener(view -> { if (navigator != null) { navigator.cancel(); From 897e9d044a67521d58602ea1764ba1441c10207f Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Fri, 30 Jan 2026 11:37:27 +0100 Subject: [PATCH 15/17] feat(account): update fragment_connect.xml --- .../java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt | 6 +----- core/src/main/res/layout/fragment_connect.xml | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt index b3a628f1..fea23c01 100644 --- a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt +++ b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt @@ -181,11 +181,7 @@ class AccountViewModel @Inject constructor( } fun isAccountStandard(): Boolean { - return accountType.get()?.equals("IVPN Standard") ?: false - } - - fun isAccountLegacy(): Boolean { - return username.get()?.startsWith("ivpn") ?: false + return plan.get() == Plan.STANDARD } fun isAccountLegacyTeam(): Boolean { diff --git a/core/src/main/res/layout/fragment_connect.xml b/core/src/main/res/layout/fragment_connect.xml index 3dba2067..7ffed116 100644 --- a/core/src/main/res/layout/fragment_connect.xml +++ b/core/src/main/res/layout/fragment_connect.xml @@ -161,7 +161,7 @@ android:letterSpacing="-0.03" android:paddingHorizontal="16dp" android:text="Renew" - android:visibility="@{account.plan.isStandard() ? View.VISIBLE : View.GONE}" + android:visibility="@{account.showAddMoreTime() ? View.VISIBLE : View.GONE}" android:textAllCaps="true" android:textColor="@color/alerts_text" android:textSize="12sp" /> @@ -203,7 +203,7 @@ android:letterSpacing="-0.03" android:paddingHorizontal="16dp" android:text="Renew" - android:visibility="@{account.plan.isStandard() ? View.VISIBLE : View.GONE}" + android:visibility="@{account.showAddMoreTime() ? View.VISIBLE : View.GONE}" android:textAllCaps="true" android:textColor="@color/alerts_text" android:textSize="12sp" /> From 4640ed898577f5e408f763d3d3f9905272705baa Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Sat, 18 Apr 2026 14:26:10 +0200 Subject: [PATCH 16/17] feat(account): update Plan.kt --- .../common/prefs/EncryptedUserPreference.kt | 11 ++++ .../core/common/session/SessionController.kt | 5 ++ .../ivpn/core/rest/data/model/ServicePlan.kt | 36 ++++++++++++ .../core/rest/data/model/ServiceStatus.java | 12 ++++ .../main/java/net/ivpn/core/v2/signup/Plan.kt | 57 ++++++++++++++----- .../core/v2/viewmodel/AccountViewModel.kt | 11 ++++ core/src/main/res/layout/content_account.xml | 6 +- 7 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java/net/ivpn/core/rest/data/model/ServicePlan.kt diff --git a/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt b/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt index 15f39dfe..7157b543 100644 --- a/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt +++ b/core/src/main/java/net/ivpn/core/common/prefs/EncryptedUserPreference.kt @@ -49,6 +49,7 @@ class EncryptedUserPreference @Inject constructor(val preference: Preference) { private const val BLANK_USERNAME = "BLANK_USERNAME" private const val BLANK_USERNAME_GENERATED_DATE = "BLANK_USERNAME_GENERATED_DATE" + private const val AVAILABLE_PLANS = "AVAILABLE_PLANS" } private val sharedPreferences: SharedPreferences = preference.accountPreference @@ -129,6 +130,16 @@ class EncryptedUserPreference @Inject constructor(val preference: Preference) { return sharedPreferences.getString(CURRENT_PLAN, "") } + fun putAvailablePlans(json: String?) { + sharedPreferences.edit() + .putString(AVAILABLE_PLANS, json) + .apply() + } + + fun getAvailablePlans(): String? { + return sharedPreferences.getString(AVAILABLE_PLANS, null) + } + fun getCapabilityMultiHop(): Boolean { // return sharedPreferences.getBoolean(USER_MULTI_HOP, false) return true diff --git a/core/src/main/java/net/ivpn/core/common/session/SessionController.kt b/core/src/main/java/net/ivpn/core/common/session/SessionController.kt index b0212503..1a5dab5d 100644 --- a/core/src/main/java/net/ivpn/core/common/session/SessionController.kt +++ b/core/src/main/java/net/ivpn/core/common/session/SessionController.kt @@ -22,6 +22,7 @@ package net.ivpn.core.common.session along with the IVPN Android app. If not, see . */ +import com.google.gson.Gson import com.wireguard.android.crypto.Keypair import net.ivpn.core.IVPNApplication import net.ivpn.core.common.Mapper @@ -34,6 +35,7 @@ import net.ivpn.core.rest.HttpClientFactory import net.ivpn.core.rest.IVPNApi import net.ivpn.core.rest.RequestListener import net.ivpn.core.rest.Responses +import net.ivpn.core.rest.data.model.ServicePlan import net.ivpn.core.rest.data.model.ServiceStatus import net.ivpn.core.rest.data.model.WireGuard import net.ivpn.core.rest.data.session.* @@ -326,6 +328,9 @@ class SessionController @Inject constructor( settings.isMultiHopEnabled = false } } + serviceStatus.availablePlans?.let { + userPreference.putAvailablePlans(Gson().toJson(it)) + } } private fun handleWireGuardResponse(wireGuard: WireGuard?, keys: Keypair?) { diff --git a/core/src/main/java/net/ivpn/core/rest/data/model/ServicePlan.kt b/core/src/main/java/net/ivpn/core/rest/data/model/ServicePlan.kt new file mode 100644 index 00000000..569ea13e --- /dev/null +++ b/core/src/main/java/net/ivpn/core/rest/data/model/ServicePlan.kt @@ -0,0 +1,36 @@ +package net.ivpn.core.rest.data.model + +/* + IVPN Android app + https://github.com/ivpn/android-app + + Created by Oleksandr Mykhailenko. + Copyright (c) 2023 IVPN Limited. + + This file is part of the IVPN Android app. + + The IVPN Android app is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any later version. + + The IVPN Android app is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along with the IVPN Android app. If not, see . +*/ + +import com.google.gson.annotations.Expose +import com.google.gson.annotations.SerializedName + +data class ServicePlan( + @SerializedName("name") + @Expose + val name: String, + + @SerializedName("device_limit") + @Expose + val deviceLimit: Int +) diff --git a/core/src/main/java/net/ivpn/core/rest/data/model/ServiceStatus.java b/core/src/main/java/net/ivpn/core/rest/data/model/ServiceStatus.java index 82aeae72..93d0e4c9 100644 --- a/core/src/main/java/net/ivpn/core/rest/data/model/ServiceStatus.java +++ b/core/src/main/java/net/ivpn/core/rest/data/model/ServiceStatus.java @@ -56,6 +56,9 @@ public class ServiceStatus { @SerializedName("device_management") @Expose private Boolean deviceManagement; + @SerializedName("available_plans") + @Expose + private List availablePlans = null; public Boolean getIsActive() { return isActive; @@ -125,6 +128,14 @@ public Boolean getDeviceManagement() { return deviceManagement; } + public List getAvailablePlans() { + return availablePlans; + } + + public void setAvailablePlans(List availablePlans) { + this.availablePlans = availablePlans; + } + @Override public String toString() { return "ServiceStatus{" + @@ -137,6 +148,7 @@ public String toString() { ", isOnFreeTrial='" + isOnFreeTrial + '\'' + ", deviceManagement='" + deviceManagement + '\'' + ", capabilities=" + capabilities + + ", availablePlans=" + availablePlans + '}'; } } \ No newline at end of file diff --git a/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt index 99609aab..675fa805 100644 --- a/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt +++ b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt @@ -22,6 +22,8 @@ package net.ivpn.core.common.billing.addfunds along with the IVPN Android app. If not, see . */ +import net.ivpn.core.rest.data.model.ServicePlan + enum class Plan( val skuPath: String, val productName: String, @@ -62,7 +64,24 @@ enum class Plan( fun getPlanTitle(): String = title - fun getPlanDesc(): String = description + fun getDeviceLimit(keyword: String, plans: List): Int { + return plans.firstOrNull { it.name.contains(keyword, ignoreCase = true) }?.deviceLimit ?: 0 + } + + fun getStandardDesc(deviceLimit: Int): String = "IVPN on $deviceLimit devices" + + fun getPlusDesc(deviceLimit: Int): String = "IVPN on $deviceLimit devices, modDNS, Mailx" + + fun getProDesc(deviceLimit: Int): String = "IVPN on $deviceLimit devices, modDNS, Mailx, Portmaster Pro" + + fun getPlanDesc(plans: List = emptyList()): String { + if (plans.isEmpty()) return description + return when (this) { + STANDARD -> getStandardDesc(getDeviceLimit("Standard", plans)) + PLUS -> getPlusDesc(getDeviceLimit("Plus", plans)) + PRO -> getProDesc(getDeviceLimit("Pro", plans)) + } + } fun isStandard(): Boolean = this == STANDARD @@ -73,12 +92,18 @@ enum class Plan( PRO -> STANDARD.title } - fun getAltDescOne(): String = - when (this) { - STANDARD -> PLUS.description - PLUS -> STANDARD.description - PRO -> STANDARD.description - } + fun getAltDescOne(plans: List = emptyList()): String { + if (plans.isEmpty()) return when (this) { + STANDARD -> PLUS.description + PLUS -> STANDARD.description + PRO -> STANDARD.description + } + return when (this) { + STANDARD -> getPlusDesc(getDeviceLimit("Plus", plans)) + PLUS -> getStandardDesc(getDeviceLimit("Standard", plans)) + PRO -> getStandardDesc(getDeviceLimit("Standard", plans)) + } + } fun getAltTitleTwo(): String = when (this) { @@ -87,10 +112,16 @@ enum class Plan( PRO -> PLUS.title } - fun getAltDescTwo(): String = - when (this) { - STANDARD -> PRO.description - PLUS -> PRO.description - PRO -> PLUS.description - } + fun getAltDescTwo(plans: List = emptyList()): String { + if (plans.isEmpty()) return when (this) { + STANDARD -> PRO.description + PLUS -> PRO.description + PRO -> PLUS.description + } + return when (this) { + STANDARD -> getProDesc(getDeviceLimit("Pro", plans)) + PLUS -> getProDesc(getDeviceLimit("Pro", plans)) + PRO -> getPlusDesc(getDeviceLimit("Plus", plans)) + } + } } diff --git a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt index fea23c01..0c1ec83b 100644 --- a/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt +++ b/core/src/main/java/net/ivpn/core/v2/viewmodel/AccountViewModel.kt @@ -27,6 +27,8 @@ import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableField import androidx.databinding.ObservableLong import androidx.lifecycle.ViewModel +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import net.ivpn.core.IVPNApplication import net.ivpn.core.common.billing.addfunds.Plan import net.ivpn.core.common.dagger.ApplicationScope @@ -35,6 +37,7 @@ import net.ivpn.core.common.qr.QRController import net.ivpn.core.common.session.SessionController import net.ivpn.core.common.session.SessionListenerImpl import net.ivpn.core.common.utils.DateUtil +import net.ivpn.core.rest.data.model.ServicePlan import net.ivpn.core.rest.data.session.SessionErrorResponse import org.slf4j.LoggerFactory import javax.inject.Inject @@ -67,6 +70,7 @@ class AccountViewModel @Inject constructor( val deviceManagement = ObservableBoolean() val deviceName = ObservableField() val plan = ObservableField() + val availablePlans = ObservableField>(emptyList()) val isExpired = ObservableBoolean() val isExpiredIn = ObservableBoolean() @@ -108,6 +112,7 @@ class AccountViewModel @Inject constructor( deviceManagement.set(getDeviceManagement()) deviceName.set(getDeviceName()) plan.set(getPlanByProductName(accountType.get())) + availablePlans.set(getAvailablePlansValue()) updateExpireData() } @@ -293,6 +298,12 @@ class AccountViewModel @Inject constructor( return Plan.getPlanByProductName(productName) } + private fun getAvailablePlansValue(): List { + val json = userPreference.getAvailablePlans() ?: return emptyList() + val type = object : TypeToken>() {}.type + return Gson().fromJson(json, type) ?: emptyList() + } + interface AccountNavigator { fun onLogOut() diff --git a/core/src/main/res/layout/content_account.xml b/core/src/main/res/layout/content_account.xml index ab7d4d11..5f21062c 100644 --- a/core/src/main/res/layout/content_account.xml +++ b/core/src/main/res/layout/content_account.xml @@ -194,7 +194,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="4dp" android:alpha="0.7" - android:text="@{account.plan.getPlanDesc()}" + android:text="@{account.plan.getPlanDesc(account.availablePlans)}" android:textColor="@color/account_text" android:visibility="@{account.isAccountNewStyle() ? View.VISIBLE : View.GONE}" app:layout_constraintStart_toStartOf="parent" @@ -348,7 +348,7 @@ android:layout_height="wrap_content" android:layout_marginTop="4dp" android:letterSpacing="-0.03" - android:text="@{account.plan.getAltDescOne()}" + android:text="@{account.plan.getAltDescOne(account.availablePlans)}" android:textColor="@color/account_plan_details_text" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -377,7 +377,7 @@ android:layout_marginTop="4dp" android:layout_marginBottom="4dp" android:letterSpacing="-0.03" - android:text="@{account.plan.getAltDescTwo()}" + android:text="@{account.plan.getAltDescTwo(account.availablePlans)}" android:textColor="@color/account_plan_details_text" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" From 98325da3719945986abf150536be56b36e27d0b3 Mon Sep 17 00:00:00 2001 From: Juraj Hilje Date: Thu, 7 May 2026 08:35:04 +0200 Subject: [PATCH 17/17] ci: update build.yml --- .github/workflows/build.yml | 3 --- .github/workflows/validate-gradle-wrapper.yaml | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e415027b..36b39501 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,9 +15,6 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # v1 - - name: Checkout submodules run: git submodule update --init --recursive diff --git a/.github/workflows/validate-gradle-wrapper.yaml b/.github/workflows/validate-gradle-wrapper.yaml index 30a0360a..2713f87b 100644 --- a/.github/workflows/validate-gradle-wrapper.yaml +++ b/.github/workflows/validate-gradle-wrapper.yaml @@ -11,4 +11,4 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/actions/wrapper-validation@v3