diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e415027b9..36b395015 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 30a0360ae..2713f87b7 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 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 837c72bc7..7157b543b 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,8 +130,19 @@ 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 sharedPreferences.getBoolean(USER_MULTI_HOP, false) + return true } fun getPaymentMethod(): String { 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 b02125033..1a5dab5db 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/common/utils/StringUtil.java b/core/src/main/java/net/ivpn/core/common/utils/StringUtil.java index 94803d7d7..2581e9ecd 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/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 000000000..569ea13ec --- /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 82aeae72a..93d0e4c90 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/connect/createSession/CreateSessionFragment.java b/core/src/main/java/net/ivpn/core/v2/connect/createSession/CreateSessionFragment.java index 7a54b5b82..8df34088d 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 @@ -100,12 +100,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); } @@ -171,11 +171,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(); 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 93ccd3e9b..428fc0d73 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/java/net/ivpn/core/v2/signup/Plan.kt b/core/src/main/java/net/ivpn/core/v2/signup/Plan.kt index dc6cdd9ce..675fa805f 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,106 @@ 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"); +import net.ivpn.core.rest.data.model.ServicePlan + +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 - } + if (productName == null) return STANDARD + + return when { + productName.contains("Plus", ignoreCase = true) -> PLUS + productName.contains("Pro", ignoreCase = true) -> PRO + else -> STANDARD + } + } + } + + fun getPlanTitle(): String = title + + 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 + + fun getAltTitleOne(): String = + when (this) { + STANDARD -> PLUS.title + PLUS -> STANDARD.title + PRO -> STANDARD.title + } + + 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) { + STANDARD -> PRO.title + PLUS -> PRO.title + PRO -> PLUS.title } - return STANDARD + 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)) } } -} \ No newline at end of file +} 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 62841f6a3..0c1ec83bb 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,13 +27,17 @@ 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 import net.ivpn.core.common.prefs.EncryptedUserPreference 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 @@ -65,6 +69,8 @@ class AccountViewModel @Inject constructor( val isActive = ObservableBoolean() val deviceManagement = ObservableBoolean() val deviceName = ObservableField() + val plan = ObservableField() + val availablePlans = ObservableField>(emptyList()) val isExpired = ObservableBoolean() val isExpiredIn = ObservableBoolean() @@ -105,6 +111,8 @@ class AccountViewModel @Inject constructor( paymentMethod = getPaymentMethodValue() deviceManagement.set(getDeviceManagement()) deviceName.set(getDeviceName()) + plan.set(getPlanByProductName(accountType.get())) + availablePlans.set(getAvailablePlansValue()) updateExpireData() } @@ -178,17 +186,22 @@ class AccountViewModel @Inject constructor( } fun isAccountStandard(): Boolean { - return accountType.get()?.equals("IVPN Standard") ?: false + return plan.get() == Plan.STANDARD } - fun isAccountLegacy(): Boolean { - return accountType.get()?.equals("Member VPN Pro Account") ?: false + fun isAccountLegacyTeam(): Boolean { + val user = username.get() ?: return false + return user.startsWith("ivpn") && accountType.get()?.contains("Member") == true } fun isAccountNewStyle(): Boolean { return isNewStyleAccount(username.get().toString()) } + fun showAddMoreTime(): Boolean { + return isAccountStandard() && isAccountNewStyle() + } + private fun clearLocalCache() { authenticated.set(false) } @@ -281,6 +294,16 @@ class AccountViewModel @Inject constructor( return userPreference.getDeviceName() } + private fun getPlanByProductName(productName: String?): Plan { + 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/bottom_sheet_legacy_standard.xml b/core/src/main/res/layout/bottom_sheet_legacy_standard.xml index 7bad55d9b..3ff6139ee 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 @@ - - - - - - - - - @@ -148,7 +149,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" /> @@ -162,9 +163,42 @@ android:letterSpacing="-0.03" android:text="@{account.accountType}" android:textColor="@color/account_text" - android:textSize="16sp" + 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"/> + + + + + app:layout_constraintTop_toBottomOf="@+id/account_label" /> @@ -185,7 +220,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" /> @@ -199,7 +234,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" /> @@ -209,7 +244,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" /> @@ -224,7 +259,7 @@ android:padding="8dp" android:text="@string/account_log_out" android:textAllCaps="true" - android:textColor="@color/account_logout" + android:textColor="@color/primary" android:textSize="14sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" @@ -243,7 +278,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.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" /> @@ -273,86 +308,81 @@ android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:background="@drawable/plan_details_background" - android:padding="12dp" + 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/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/layout/fragment_connect.xml b/core/src/main/res/layout/fragment_connect.xml index 52efc9b33..7ffed1166 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.showAddMoreTime() ? 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.showAddMoreTime() ? 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 e3f319882..de2f0299e 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -364,7 +364,7 @@ Retry Visit Device Management Enable Device Management - Switch to IVPN Pro + Upgrade your subscription Cancel login Error @@ -490,6 +490,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 @@ -654,8 +657,9 @@ 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 Your current location 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 2a46e6346..462de17ee 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 } 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 360b97e08..450918b62 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/java/net/ivpn/client/signup/SignUpViewModel.kt b/store/src/main/java/net/ivpn/client/signup/SignUpViewModel.kt index e7cf30daa..210eca3b0 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) 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 1b7521539..ef7f72463 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" /> diff --git a/store/src/main/res/navigation/store_nav_graph.xml b/store/src/main/res/navigation/store_nav_graph.xml index 19446e9a2..08422f758 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 @@ +