Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
dheshanm committed Apr 27, 2023
2 parents 500f18a + 4c53d51 commit 57c0a80
Show file tree
Hide file tree
Showing 25 changed files with 762 additions and 117 deletions.
7 changes: 5 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ android {
applicationId = "com.mohandass.botforge"
minSdk = 28
targetSdk = 33
versionCode = 30
versionName = "1.3.0"
versionCode = 31
versionName = "1.3.1"

vectorDrawables.useSupportLibrary = true

Expand Down Expand Up @@ -109,6 +109,7 @@ dependencies {
val coilVersion = "2.3.0"
val markwonVersion = "4.6.2"
val leakCanaryVersion = "2.10"
val okioVersion = "3.3.0"

val playServicesAuthVersion = "20.5.0"

Expand Down Expand Up @@ -200,6 +201,8 @@ dependencies {
// LeakCanary for memory leak detection
// https://square.github.io/leakcanary/
debugImplementation("com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion")

implementation("com.squareup.okio:okio:$okioVersion")
}

// Dependency Injection with Hilt
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/mohandass/botforge/AppRoutes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ sealed class AppRoutes(val route: String) {
object Settings : MainRoutes("main_settings")
object ApiKeySettings : MainRoutes("main_settings_api_key")
object ApiUsageSettings : MainRoutes("main_settings_api_usage")
object ApiAdvancedSettings : MainRoutes("main_settings_api_advanced")
object ManageAccountSettings : MainRoutes("main_settings_manage_account")
object OpenSourceLicenses : MainRoutes("main_settings_open_source_licenses")
object IconCredits : MainRoutes("main_settings_icon_credits")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
Expand Down Expand Up @@ -46,7 +45,7 @@ fun PersonaListUi(
personaViewModel: PersonaViewModel = hiltViewModel(),
browseViewModel: BrowseViewModel = hiltViewModel(),
) {
val personas by personaViewModel.personas.observeAsState(initial = emptyList())
val personas = personaListViewModel.personas
val matchedPersonas = personaListViewModel.matchedPersonas

var showDeleteAllPersonaDialog by personaListViewModel.showDeleteAllPersonaDialog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,22 @@ class PersonaListViewModel @Inject constructor(
private val botService: BotService,
private val logger: Logger
) : ViewModel() {
private var _personas = mutableStateListOf<Persona>()
val personas = personaRepository.personas.asLiveData()
var personas = mutableStateListOf<Persona>()
private val _personas = personaRepository.personas.asLiveData()

// Reference:
// https://stackoverflow.com/questions/48396092/should-i-include-lifecycleowner-in-viewmodel
private val observer: (List<Persona>) -> Unit = {
_personas.clear()
_personas.addAll(it)
personas.clear()
personas.addAll(it)
}

init {
personas.observeForever(observer)
_personas.observeForever(observer)
}

override fun onCleared() {
personas.removeObserver(observer)
_personas.removeObserver(observer)
super.onCleared()
}

Expand All @@ -72,7 +72,7 @@ class PersonaListViewModel @Inject constructor(
if (searchQuery.value.isEmpty() || searchQuery.value.isBlank()) {
return
}
personas.value!!.filterTo(matchedPersonas) { persona ->
_personas.value!!.filterTo(matchedPersonas) { persona ->

persona.name.contains(searchQuery.value, ignoreCase = true) ||
persona.alias.contains(searchQuery.value, ignoreCase = true) ||
Expand All @@ -99,16 +99,16 @@ class PersonaListViewModel @Inject constructor(
fun deletePersona(uuid: String) {
var deleteJob: Job = Job()

val persona = _personas.find { it.uuid == uuid }
val persona = personas.find { it.uuid == uuid }
if (persona != null) {
// Remove persona from the list
_personas.remove(persona)
personas.remove(persona)

SnackbarManager.showMessageWithAction(
R.string.deleted_persona,
R.string.undo
) {
_personas.add(persona)
personas.add(persona)
deleteJob.cancel()
}

Expand All @@ -124,7 +124,7 @@ class PersonaListViewModel @Inject constructor(

fun fetchBots() {
logger.logVerbose(TAG, "fetchBots")
for (persona in personas.value!!) {
for (persona in _personas.value!!) {
viewModelScope.launch {
bots[persona.parentUuid] = botService.getBot(persona.parentUuid)
logger.logVerbose(
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/com/mohandass/botforge/common/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ package com.mohandass.botforge.common

class Constants {
companion object {
const val DEFAULT_API_TIMEOUT = 60
const val MAX_API_TIMEOUT = 300
const val MIN_API_TIMEOUT = 15

const val ANIMATION_DURATION = 400
const val ANIMATION_OFFSET = 400

Expand All @@ -16,4 +20,4 @@ class Constants {

const val MAX_SENSITIVITY_THRESHOLD = 5f
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,11 @@ interface OpenAiService {
n: Int = 1,
imageSize: ImageSize = ImageSize.is256x256,
): List<ImageURL>

@OptIn(BetaOpenAI::class)
suspend fun generateImageVariant(
original: ByteArray,
n: Int,
imageSize: ImageSize,
): List<ImageURL>
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ package com.mohandass.botforge.common.services.implementation
import com.aallam.openai.api.BetaOpenAI
import com.aallam.openai.api.chat.ChatCompletion
import com.aallam.openai.api.chat.ChatCompletionRequest
import com.aallam.openai.api.file.FileSource
import com.aallam.openai.api.http.Timeout
import com.aallam.openai.api.image.ImageCreation
import com.aallam.openai.api.image.ImageSize
import com.aallam.openai.api.image.ImageURL
import com.aallam.openai.api.image.ImageVariation
import com.aallam.openai.api.logging.LogLevel
import com.aallam.openai.api.model.ModelId
import com.aallam.openai.client.OpenAI
import com.aallam.openai.client.OpenAIConfig
import com.mohandass.botforge.chat.model.Message
import com.mohandass.botforge.chat.model.MessageMetadata
import com.mohandass.botforge.chat.model.Role
import com.mohandass.botforge.common.services.OpenAiService
import com.mohandass.botforge.common.services.Logger
import com.mohandass.botforge.common.services.OpenAiService
import com.mohandass.botforge.settings.service.SharedPreferencesService
import okio.Source
import okio.source
import kotlin.time.Duration.Companion.seconds

/**
* An implementation of the OpenAiService interface
Expand All @@ -38,7 +46,13 @@ class OpenAiServiceImpl private constructor(
throw Exception("No API key found")
}

return OpenAI(apiKey)
val config = OpenAIConfig(
token = apiKey,
logLevel = LogLevel.Info,
timeout = Timeout(socket = sharedPreferencesService.getApiTimeout().seconds),
)

return OpenAI(config)
}

@OptIn(BetaOpenAI::class)
Expand Down Expand Up @@ -101,6 +115,21 @@ class OpenAiServiceImpl private constructor(
)
)

// Update usage image count
when (imageSize) {
ImageSize.is256x256 -> {
sharedPreferencesService.incrementUsageImageSmallCount(n)
}

ImageSize.is512x512 -> {
sharedPreferencesService.incrementUsageImageMediumCount(n)
}

ImageSize.is1024x1024 -> {
sharedPreferencesService.incrementUsageImageLargeCount(n)
}
}

logger.logVerbose(TAG, "generateImage() $images")
return images
} catch (e: Exception) {
Expand All @@ -109,6 +138,48 @@ class OpenAiServiceImpl private constructor(
}
}

@OptIn(BetaOpenAI::class)
override suspend fun generateImageVariant(
original: ByteArray,
n: Int,
imageSize: ImageSize,
): List<ImageURL> {
logger.logVerbose(TAG, "generateImageVariant()")
try {
val source: Source = original.inputStream().source()

val fileSource = FileSource(name = "original.png", source = source)
val images = getClient().imageURL( // or openAI.imageJSON
variation = ImageVariation(
image = fileSource,
n = n,
size = imageSize
)
)

// Update usage image count
when (imageSize) {
ImageSize.is256x256 -> {
sharedPreferencesService.incrementUsageImageSmallCount(n)
}

ImageSize.is512x512 -> {
sharedPreferencesService.incrementUsageImageMediumCount(n)
}

ImageSize.is1024x1024 -> {
sharedPreferencesService.incrementUsageImageLargeCount(n)
}
}

logger.logVerbose(TAG, "generateImageVariant() $images")
return images
} catch (e: Exception) {
logger.logError(TAG, "generateImageVariant() ${e.printStackTrace()}", e)
throw e
}
}

companion object {
private const val TAG = "OpenAiService"

Expand Down
51 changes: 51 additions & 0 deletions app/src/main/java/com/mohandass/botforge/common/ui/MainUi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.mohandass.botforge.AppState
import com.mohandass.botforge.chat.ui.PersonaUi
import com.mohandass.botforge.chat.ui.components.header.top.TopBar
import com.mohandass.botforge.common.Constants
import com.mohandass.botforge.settings.ui.ApiAdvancedUi
import com.mohandass.botforge.settings.ui.ApiKeyUi
import com.mohandass.botforge.settings.ui.IconCreditsUi
import com.mohandass.botforge.settings.ui.ManageAccountUi
Expand Down Expand Up @@ -265,6 +266,56 @@ fun MainUi(
) {
ApiUsageUi(settingsViewModel = hiltViewModel())
}
composable(
route = AppRoutes.MainRoutes.ApiAdvancedSettings.route,
enterTransition = {
slideInHorizontally(
initialOffsetX = { Constants.ANIMATION_OFFSET },
animationSpec = tween(
durationMillis = Constants.ANIMATION_DURATION,
easing = FastOutSlowInEasing
)
) + fadeIn(
animationSpec = tween(Constants.ANIMATION_DURATION)
)

},
exitTransition = {
slideOutHorizontally(
targetOffsetX = { -Constants.ANIMATION_OFFSET },
animationSpec = tween(
durationMillis = Constants.ANIMATION_DURATION,
easing = FastOutSlowInEasing
)
) + fadeOut(
animationSpec = tween(Constants.ANIMATION_DURATION)
)
},
popEnterTransition = {
slideInHorizontally(
initialOffsetX = { -Constants.ANIMATION_OFFSET },
animationSpec = tween(
durationMillis = Constants.ANIMATION_DURATION,
easing = FastOutSlowInEasing
)
) + fadeIn(
animationSpec = tween(Constants.ANIMATION_DURATION)
)
},
popExitTransition = {
slideOutHorizontally(
targetOffsetX = { Constants.ANIMATION_OFFSET },
animationSpec = tween(
durationMillis = Constants.ANIMATION_DURATION,
easing = FastOutSlowInEasing
)
) + fadeOut(
animationSpec = tween(Constants.ANIMATION_DURATION)
)
}
) {
ApiAdvancedUi()
}
composable(
route = AppRoutes.MainRoutes.ManageAccountSettings.route,
enterTransition = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class ImageSizeInternal(size: ImageSize) {

override fun toString(): String {
return when (this) {
is256x256 -> "256x256"
is512x512 -> "512x512"
is1024x1024 -> "1024x1024"
is256x256 -> "Small (256x256)"
is512x512 -> "Medium (512x512)"
is1024x1024 -> "Large (1024x1024)"
else -> {
throw IllegalArgumentException("Unknown image size: $this")
}
Expand Down
13 changes: 12 additions & 1 deletion app/src/main/java/com/mohandass/botforge/image/ui/ImageUi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,17 @@ fun ImageUi(
}
}

IconButton(
onClick = {
imageViewModel.generateImageVariant()
}
) {
Icon(
painter = painterResource(id = R.drawable.baseline_refresh_24),
contentDescription = stringResource(id = R.string.generate_variant),
)
}

IconButton(
onClick = {
imageViewModel.shareImage(context)
Expand Down Expand Up @@ -405,7 +416,7 @@ fun ImageUi(
NumberPicker(
modifier = Modifier
.weight(1f),
n = n,
numberAsString = n.toString(),
onIncrement = {
if (n < Constants.MAX_IMAGE_GENERATION_COUNT) {
n++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import com.mohandass.botforge.R
@Composable
fun NumberPicker(
modifier: Modifier = Modifier,
n: Int,
numberAsString: String,
onIncrement: () -> Unit,
onDecrement: () -> Unit,
) {
Expand Down Expand Up @@ -69,7 +69,7 @@ fun NumberPicker(
disabledContentColor = MaterialTheme.colorScheme.primary
)
) {
Text(text = n.toString())
Text(text = numberAsString)
}

OutlinedButton(
Expand Down Expand Up @@ -98,7 +98,7 @@ fun NumberPickerPreview() {
}

NumberPicker(
n = n,
numberAsString = n.toString(),
onIncrement = {
n += 1
},
Expand Down
Loading

0 comments on commit 57c0a80

Please sign in to comment.