Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,7 @@ dependencies {

// Api
implementation(project(":backend-api"))

// Preferences
implementation(libs.ksprefs)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package com.github.ai.simplesplit.android.data.api

import arrow.core.Either
import arrow.core.Some
import com.github.ai.simplesplit.android.data.json.JsonSerializer
import com.github.ai.simplesplit.android.data.settings.Settings
import com.github.ai.simplesplit.android.model.exception.ApiException
import com.github.ai.simplesplit.android.utils.atomicReference
import com.github.ai.split.api.request.PostExpenseRequest
import com.github.ai.split.api.request.PostGroupRequest
import com.github.ai.split.api.request.PostMemberRequest
Expand All @@ -16,13 +19,31 @@ import com.github.ai.split.api.response.PostGroupResponse
import com.github.ai.split.api.response.PostMemberResponse
import com.github.ai.split.api.response.PutExpenseResponse
import com.github.ai.split.api.response.PutGroupResponse
import io.ktor.client.HttpClient

class ApiClient(
private val httpClient: HttpClient,
private val baseUrl: String = SERVER_URL
private val jsonSerializer: JsonSerializer,
private val settings: Settings
) {

private var baseUrl by atomicReference(settings.serverUrl)
private var httpClient by atomicReference(
HttpClientFactory.createHttpClient(
jsonSerializer = jsonSerializer,
isSslVerificationEnabled = settings.isSslVerificationEnabled
)
)

fun updateHttpClient() {
httpClient = HttpClientFactory.createHttpClient(
jsonSerializer = jsonSerializer,
isSslVerificationEnabled = settings.isSslVerificationEnabled
)
}

fun updateServerUrl() {
baseUrl = settings.serverUrl
}

suspend fun getGroups(
uids: List<String>,
passwords: List<String>
Expand Down Expand Up @@ -104,6 +125,7 @@ class ApiClient(
)

companion object {
const val SERVER_URL = "https://api.simplesplitapp.link"
const val PROD_SERVER_URL = "https://api.simplesplitapp.link"
const val DEBUG_SERVER_URL = "http://10.0.2.2:8080"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.ai.simplesplit.android.data.settings

enum class SettingKey(
val key: String
) {
IS_SSL_VERIFICATION_ENABLE(
key = "isSslVerificationEnabled"
),
SERVER_URL(
key = "serverUrl"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.ai.simplesplit.android.data.settings

import android.content.Context
import com.cioccarellia.ksprefs.KsPrefs
import com.github.ai.simplesplit.android.data.api.ApiClient
import com.github.ai.simplesplit.android.data.settings.SettingKey.IS_SSL_VERIFICATION_ENABLE
import com.github.ai.simplesplit.android.data.settings.SettingKey.SERVER_URL

interface Settings {
var serverUrl: String
var isSslVerificationEnabled: Boolean
}

class SettingsImpl(
context: Context
) : Settings {

private val prefs = KsPrefs(context.applicationContext)

override var isSslVerificationEnabled: Boolean
get() = prefs.pull(IS_SSL_VERIFICATION_ENABLE.key, false)
set(value) {
prefs.push(IS_SSL_VERIFICATION_ENABLE.key, value)
}

override var serverUrl: String
get() = prefs.pull(SERVER_URL.key, ApiClient.PROD_SERVER_URL)
set(value) {
prefs.push(SERVER_URL.key, value)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.github.ai.simplesplit.android.di

import com.github.ai.simplesplit.android.data.api.ApiClient
import com.github.ai.simplesplit.android.data.api.HttpClientFactory
import com.github.ai.simplesplit.android.data.database.AppDatabase
import com.github.ai.simplesplit.android.data.json.JsonSerializer
import com.github.ai.simplesplit.android.data.repository.ExpenseRepository
import com.github.ai.simplesplit.android.data.repository.GroupCredentialsRepository
import com.github.ai.simplesplit.android.data.repository.GroupRepository
import com.github.ai.simplesplit.android.data.repository.MemberRepository
import com.github.ai.simplesplit.android.data.settings.Settings
import com.github.ai.simplesplit.android.data.settings.SettingsImpl
import com.github.ai.simplesplit.android.domain.usecase.CreateExportUrlUseCase
import com.github.ai.simplesplit.android.domain.usecase.CreateGroupUrlUseCase
import com.github.ai.simplesplit.android.domain.usecase.ParseGroupUrlUseCase
Expand Down Expand Up @@ -40,6 +41,9 @@ import com.github.ai.simplesplit.android.presentation.screens.groupEditor.model.
import com.github.ai.simplesplit.android.presentation.screens.groups.GroupsInteractor
import com.github.ai.simplesplit.android.presentation.screens.groups.GroupsViewModel
import com.github.ai.simplesplit.android.presentation.screens.root.RootViewModel
import com.github.ai.simplesplit.android.presentation.screens.settings.SettingsInteractor
import com.github.ai.simplesplit.android.presentation.screens.settings.SettingsViewModel
import com.github.ai.simplesplit.android.presentation.screens.settings.cells.SettingsCellFactory
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.bind
import org.koin.dsl.module
Expand All @@ -50,15 +54,15 @@ object AndroidAppModule {
// Core
singleOf(::ThemeProviderImpl).bind(ThemeProvider::class)
singleOf(::ResourceProviderImpl).bind(ResourceProvider::class)
singleOf(::SettingsImpl).bind(Settings::class)

// Database
single { AppDatabase.buildDatabase(get()) }
single { get<AppDatabase>().groupCredentialsDao() }

// Api
singleOf(::JsonSerializer)
single { HttpClientFactory.createHttpClient(get(), isSslVerificationEnabled = true) }
single { ApiClient(get()) }
singleOf(::ApiClient)

// Repositories
singleOf(::GroupRepository)
Expand All @@ -77,10 +81,12 @@ object AndroidAppModule {
singleOf(::GroupEditorInteractor)
singleOf(::ExpenseEditorInteractor)
singleOf(::CheckoutGroupInteractor)
singleOf(::SettingsInteractor)

// CellFactories
singleOf(::GroupDetailsCellFactory)
singleOf(::ExpenseDetailsDialogCellFactory)
singleOf(::SettingsCellFactory)

// Router
singleOf(::RouterImpl).bind(Router::class)
Expand All @@ -94,6 +100,14 @@ object AndroidAppModule {
get()
)
}
factory {
SettingsViewModel(
get(),
get(),
get(),
get()
)
}
factory { (args: GroupDetailsArgs) ->
GroupDetailsViewModel(
get(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.github.ai.simplesplit.android.domain.usecase

import com.github.ai.simplesplit.android.data.api.ApiClient
import com.github.ai.simplesplit.android.data.settings.Settings
import com.github.ai.simplesplit.android.model.db.GroupCredentials

class CreateExportUrlUseCase {
class CreateExportUrlUseCase(
private val settings: Settings
) {

fun createUrl(credentials: GroupCredentials): String {
return "%s/export/%s.csv?password=%s".format(
ApiClient.SERVER_URL,
settings.serverUrl,
credentials.groupUid,
credentials.password
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.github.ai.simplesplit.android.domain.usecase

import com.github.ai.simplesplit.android.data.api.ApiClient
import com.github.ai.simplesplit.android.data.settings.Settings
import com.github.ai.simplesplit.android.model.db.GroupCredentials

class CreateGroupUrlUseCase {
class CreateGroupUrlUseCase(
private val settings: Settings
) {

fun createUrl(credentials: GroupCredentials): String {
return "%s/group?ids=%s&passwords=%s".format(
ApiClient.SERVER_URL,
settings.serverUrl,
credentials.groupUid,
credentials.password
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.github.ai.simplesplit.android.presentation.core.compose.preview.ThemedPreview
import com.github.ai.simplesplit.android.presentation.core.compose.theme.AppIcon
import com.github.ai.simplesplit.android.presentation.core.compose.theme.ElementMargin
import com.github.ai.simplesplit.android.presentation.core.compose.theme.LightTheme
import com.github.ai.simplesplit.android.presentation.core.compose.theme.MediumMargin

@Composable
fun AppDropdownField(
Expand All @@ -47,7 +49,11 @@ fun AppDropdownField(
Icon(
imageVector = AppIcon.EXPAND_MORE.vector,
contentDescription = null,
modifier = Modifier.clickable { expanded = true }
modifier = Modifier
.clickable(
onClick = { expanded = true }
)
.padding(MediumMargin)
)
},
modifier = modifier.clickable { expanded = true }
Expand All @@ -74,7 +80,10 @@ fun AppDropdownField(
text = error.orEmpty(),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(start = 16.dp, top = 4.dp)
modifier = Modifier
.padding(
start = ElementMargin
)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import com.github.ai.simplesplit.android.presentation.core.compose.theme.AppThem

enum class TopBarMenuItem {
DONE,
MENU
MENU,
SETTINGS
}

@OptIn(ExperimentalMaterial3Api::class)
Expand Down Expand Up @@ -75,5 +76,6 @@ private fun TopBarMenuItem.getImageVector(): ImageVector {
return when (this) {
TopBarMenuItem.DONE -> AppIcon.CHECK.vector
TopBarMenuItem.MENU -> AppIcon.MENU.vector
TopBarMenuItem.SETTINGS -> AppIcon.SETTINGS.vector
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.ai.simplesplit.android.presentation.core.compose.cells

import kotlinx.coroutines.flow.MutableStateFlow

abstract class MutableCellViewModel<T : CellModel>(
initialModel: T
) : CellViewModel {

val observableModel = MutableStateFlow(initialModel)

override val model: T
get() = observableModel.value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.ai.simplesplit.android.presentation.core.compose.cells.model

import com.github.ai.simplesplit.android.presentation.core.compose.cells.CellEvent

sealed interface DropDownCellEvent : CellEvent {
data class OnOptionSelect(
val cellId: String,
val selectedOption: String
) : DropDownCellEvent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.ai.simplesplit.android.presentation.core.compose.cells.model

import androidx.compose.runtime.Immutable
import com.github.ai.simplesplit.android.presentation.core.compose.cells.CellModel

@Immutable
data class DropDownCellModel(
override val id: String,
val title: String,
val options: List<String>,
val selectedOption: String
) : CellModel
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.ai.simplesplit.android.presentation.core.compose.cells.model

import com.github.ai.simplesplit.android.presentation.core.compose.cells.CellEvent

interface SwitchCellEvent : CellEvent {

data class OnCheckChanged(
val cellId: String,
val isChecked: Boolean
) : SwitchCellEvent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.ai.simplesplit.android.presentation.core.compose.cells.model

import androidx.compose.runtime.Immutable
import com.github.ai.simplesplit.android.presentation.core.compose.cells.CellModel

@Immutable
data class SwitchCellModel(
override val id: String,
val title: String,
val description: String,
val isChecked: Boolean,
val isEnabled: Boolean
) : CellModel
Loading