Skip to content

Commit

Permalink
Android u5 (12): in-app purchase (#36)
Browse files Browse the repository at this point in the history
* Android u5 (12): in-app purchase

* upgdated gradle

* updated build.gradle.kts
  • Loading branch information
64bit authored Oct 28, 2024
1 parent e9f6b2f commit 96c261f
Show file tree
Hide file tree
Showing 13 changed files with 745 additions and 23 deletions.
31 changes: 20 additions & 11 deletions upvpn-android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ android {
applicationId = "app.upvpn.upvpn"
minSdk = 24
targetSdk = 34
versionCode = 10
versionName = "u4"
versionCode = 12
versionName = "u5"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -68,7 +68,10 @@ android {
debug {
isMinifyEnabled = false
versionNameSuffix = ".debug"
val baseUrl = gradleLocalProperties(rootDir).getProperty("baseUrl", "https://upvpn.dev")
val baseUrl = gradleLocalProperties(rootDir, providers).getProperty(
"baseUrl",
"https://upvpn.dev"
)
buildConfigField(
"String",
"UPVPN_BASE_URL",
Expand Down Expand Up @@ -138,21 +141,22 @@ android {
}

dependencies {
val navVersion = "2.8.2"
val navVersion = "2.8.3"
val roomVersion = "2.6.1"
val sandwichVersion = "1.3.9"
val billingVersion = "7.1.1"

implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.6")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.6")
implementation("androidx.activity:activity-compose:1.9.2")
implementation(platform("androidx.compose:compose-bom:2024.09.03"))
implementation("androidx.compose.ui:ui:1.7.3")
implementation("androidx.compose.ui:ui-graphics:1.7.3")
implementation("androidx.compose.ui:ui-tooling-preview:1.7.3")
implementation("androidx.activity:activity-compose:1.9.3")
implementation(platform("androidx.compose:compose-bom:2024.10.00"))
implementation("androidx.compose.ui:ui:1.7.4")
implementation("androidx.compose.ui:ui-graphics:1.7.4")
implementation("androidx.compose.ui:ui-tooling-preview:1.7.4")
implementation("androidx.compose.material3:material3:1.3.0")
implementation("androidx.compose.material3:material3-window-size-class:1.3.0")
implementation("androidx.compose.material:material-icons-extended:1.7.3")
implementation("androidx.compose.material:material-icons-extended:1.7.4")
implementation("androidx.navigation:navigation-compose:$navVersion")
implementation("com.google.accompanist:accompanist-adaptive:0.32.0")

Expand All @@ -176,6 +180,11 @@ dependencies {
// Network response
implementation("com.github.skydoves:sandwich:$sandwichVersion")

// IAP
implementation("com.android.billingclient:billing:$billingVersion")
implementation("com.android.billingclient:billing-ktx:$billingVersion")


// for java.time
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2")

Expand All @@ -184,7 +193,7 @@ dependencies {
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2024.09.03"))
androidTestImplementation(platform("androidx.compose:compose-bom:2024.10.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class DefaultAppContainer(private val context: Context) : AppContainer {
}

override val planRepository: PlanRepository by lazy {
DefaultPlanRepository(retrofitService)
DefaultPlanRepository(retrofitService, vpnDatabase)
}

override val serviceConnectionManager: VPNServiceConnectionManager by lazy {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package app.upvpn.upvpn.data

import app.upvpn.upvpn.data.db.VPNDatabase
import app.upvpn.upvpn.model.UserPlan
import app.upvpn.upvpn.network.VPNApiService
import app.upvpn.upvpn.network.toResult
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import java.util.UUID

interface PlanRepository {
suspend fun getUserPlan(): Result<UserPlan, String>
suspend fun getEmailAndDeviceId(): Result<EmailAndDeviceId, String>
}

data class EmailAndDeviceId(
val email: String,
val deviceId: UUID,
)

class DefaultPlanRepository(
private val vpnApiService: VPNApiService,
private val vpnDatabase: VPNDatabase,
) : PlanRepository {

private val tag = "DefaultPlanRepository"
Expand All @@ -20,4 +31,15 @@ class DefaultPlanRepository(
return vpnApiService.getUserPlan().toResult()
.mapError { e -> e.message }
}

override suspend fun getEmailAndDeviceId(): Result<EmailAndDeviceId, String> {
val user = vpnDatabase.userDao().getUser()
val device = vpnDatabase.deviceDao().getDevice()

return if (user != null && device != null) {
Ok(EmailAndDeviceId(user.email, device.uniqueId))
} else {
Err("user or device not found")
}
}
}
20 changes: 18 additions & 2 deletions upvpn-android/app/src/main/kotlin/app/upvpn/upvpn/ui/VPNApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.window.layout.DisplayFeature
import app.upvpn.upvpn.BuildConfig
import app.upvpn.upvpn.model.Location
import app.upvpn.upvpn.ui.components.LocationsPopup
import app.upvpn.upvpn.ui.components.PlanBottomSheet
import app.upvpn.upvpn.ui.components.VPNLayout
import app.upvpn.upvpn.ui.screens.HelpScreen
import app.upvpn.upvpn.ui.screens.HomeScreen
Expand Down Expand Up @@ -64,6 +65,8 @@ fun VPNApp(
val uiState = authViewModel.uiState.collectAsStateWithLifecycle()
val signOutUiState = authViewModel.signOutUiState.collectAsStateWithLifecycle()

var showPlanSheet by remember { mutableStateOf(false) }

val (startDestination, userEmail) = when (uiState.value.signInState) {
is SignInState.SignedIn -> {
VPNScreen.Home.name to (uiState.value.signInState as SignInState.SignedIn).email
Expand Down Expand Up @@ -116,12 +119,19 @@ fun VPNApp(

// show alert dialog for errors
vpnNotifications.value.forEach { notification ->
val dismissNotification = {
homeVM.ackVpnNotification(notification)
if (notification.msg.lowercase().contains("insufficient balance")) {
showPlanSheet = true
}
}

AlertDialog(
onDismissRequest = { homeVM.ackVpnNotification(notification) },
onDismissRequest = dismissNotification,
title = { Text("Oh No") },
text = { Text(notification.msg) },
confirmButton = {
TextButton(onClick = { homeVM.ackVpnNotification(notification) }) {
TextButton(onClick = dismissNotification) {
Text("OK")
}
}
Expand Down Expand Up @@ -193,6 +203,12 @@ fun VPNApp(
onRefresh = locationVM::onRefresh
)

PlanBottomSheet(
showPlanSheet = showPlanSheet,
dismissPlanSheet = { showPlanSheet = false },
planState = planState.value,
refresh = { planVM.fetchPlan() })

NavHost(navController = navController, startDestination = startDestination) {
composable(route = VPNScreen.Login.name) {
SignInScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import app.upvpn.upvpn.VPNApplication
import app.upvpn.upvpn.ui.viewmodels.AuthViewModel
import app.upvpn.upvpn.ui.viewmodels.BillingViewModel
import app.upvpn.upvpn.ui.viewmodels.HomeViewModel
import app.upvpn.upvpn.ui.viewmodels.LocationViewModel
import app.upvpn.upvpn.ui.viewmodels.PlanViewModel
Expand Down Expand Up @@ -40,5 +41,11 @@ object VPNAppViewModelProvider {
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as VPNApplication)
PlanViewModel(application.container.planRepository)
}

initializer {
val application =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as VPNApplication)
BillingViewModel(application.container.planRepository)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package app.upvpn.upvpn.ui.components

import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import app.upvpn.upvpn.ui.screens.PlanScreen
import app.upvpn.upvpn.ui.viewmodels.PlanState

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PlanBottomSheet(
showPlanSheet: Boolean,
dismissPlanSheet: () -> Unit,
planState: PlanState,
refresh: () -> Unit,
) {
val sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = false,
)

if (showPlanSheet) {
ModalBottomSheet(
modifier = Modifier.fillMaxHeight(),
sheetState = sheetState,
onDismissRequest = dismissPlanSheet
) {
PlanScreen(planState, refresh, dismissPlanSheet)
}
}
}
Loading

0 comments on commit 96c261f

Please sign in to comment.