Skip to content

ai: show less options (fixes #5242) #5243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
136 changes: 103 additions & 33 deletions app/src/main/java/org/ole/planet/myplanet/ui/chat/ChatDetailFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ package org.ole.planet.myplanet.ui.chat

import android.content.Context
import android.content.SharedPreferences
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.text.*
import android.view.*
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TableRow
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.*
import com.google.android.material.button.MaterialButton
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import io.realm.Realm
Expand Down Expand Up @@ -85,7 +87,7 @@ class ChatDetailFragment : Fragment() {
}

fragmentChatDetailBinding.buttonGchatSend.setOnClickListener {
val aiProvider = AiProvider(name = aiName, model = aiModel)
val aiProvider = AiProvider(name = aiName.lowercase(), model = aiModel)
fragmentChatDetailBinding.textGchatIndicator.visibility = View.GONE
if (TextUtils.isEmpty("${fragmentChatDetailBinding.editGchatMessage.text}".trim())) {
fragmentChatDetailBinding.textGchatIndicator.visibility = View.VISIBLE
Expand Down Expand Up @@ -178,7 +180,7 @@ class ChatDetailFragment : Fragment() {
}

private fun checkAiProviders() {
val updateUrl = "${settings.getString("serverURL", "")}"
val updateUrl = settings.getString("serverURL", "") ?: ""
val serverUrlMapper = ServerUrlMapper(requireContext())
val mapping = serverUrlMapper.processUrl(updateUrl)

Expand All @@ -190,7 +192,6 @@ class ChatDetailFragment : Fragment() {
mapping.alternativeUrl.let { alternativeUrl ->
val uri = Uri.parse(updateUrl)
val editor = settings.edit()

serverUrlMapper.updateUrlPreferences(editor, uri, alternativeUrl, mapping.primaryUrl, settings)
}
}
Expand All @@ -206,10 +207,16 @@ class ChatDetailFragment : Fragment() {
try {
val responseString = responseBody.string()
val aiProvidersResponse: Map<String, Boolean> = Gson().fromJson(
responseString,
object : TypeToken<Map<String, Boolean>>() {}.type
responseString, object : TypeToken<Map<String, Boolean>>() {}.type
)
updateAIButtons(aiProvidersResponse)

val primaryModel = if (aiProvidersResponse.containsKey("openai")) {
"openai"
} else {
aiProvidersResponse.keys.firstOrNull() ?: "openai"
}

updateAIButtons(aiProvidersResponse, primaryModel)
} catch (e: JsonSyntaxException) {
e.printStackTrace()
onFailError()
Expand All @@ -232,56 +239,119 @@ class ChatDetailFragment : Fragment() {
}
}

private fun updateAIButtons(aiProvidersResponse: Map<String, Boolean>) {
private fun updateAIButtons(aiProvidersResponse: Map<String, Boolean>, primaryModel: String) {
if (!isAdded || context == null) return

val aiTableRow = fragmentChatDetailBinding.aiTableRow
aiTableRow.removeAllViews()
val aiContainer = fragmentChatDetailBinding.aiContainer
aiContainer.removeAllViews()

val currentContext = requireContext()
val modelsString = settings.getString("ai_models", null)

val modelsMap: Map<String, String> = if (modelsString != null) {
Gson().fromJson(modelsString, object : TypeToken<Map<String, String>>() {}.type)
} else {
emptyMap()
}

val providersMap = aiProvidersResponse.filter { it.value }

if (providersMap.isEmpty()) return

providersMap.keys.forEachIndexed { index, providerName ->
val modelName = modelsMap[providerName.lowercase()] ?: "default-model"

val button = Button(currentContext).apply {
text = providerName.lowercase(Locale.getDefault())
setTextColor(ContextCompat.getColor(currentContext, R.color.md_black_1000))
textSize = 18f
setTypeface(null, Typeface.BOLD)
setPadding(16, 8, 16, 8)
isAllCaps = false
setBackgroundColor(ContextCompat.getColor(currentContext, R.color.disable_color))
setOnClickListener { selectAI(this, providerName, modelName) }
val displayedProviders = listOf(primaryModel)
val hiddenProviders = providersMap.keys.filter { it != primaryModel }

displayedProviders.forEach { providerName ->
val modelName = modelsMap[providerName.lowercase()] ?: ""
val button = createStyledAIButton(providerName.lowercase(), modelName.lowercase(), currentContext)
aiContainer.addView(button)
}

if (hiddenProviders.isNotEmpty()) {
val moreButton = createDropdownMenu(hiddenProviders, modelsMap, currentContext, aiContainer)
aiContainer.addView(moreButton)
}

aiContainer.getChildAt(0)?.performClick()
}

private fun createStyledAIButton(providerName: String, modelName: String, context: Context): MaterialButton {
return MaterialButton(context, null, R.attr.materialButtonOutlinedStyle).apply {
text = providerName.capitalize(Locale.getDefault())
setTextColor(ContextCompat.getColor(context, R.color.md_black_1000))
textSize = 18f
cornerRadius = 20
strokeWidth = 2
setPadding(25, 21, 25, 21)
setBackgroundColor(ContextCompat.getColor(context, R.color.disable_color))

layoutParams = TableRow.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT
).apply {
setMargins(14, 6, 14, 6)
}
setOnClickListener {
selectAI(this, providerName, modelName)
}

aiTableRow.addView(button)
setOnLongClickListener {
(parent as? TableRow)?.removeView(this)
true
}
}
}

private fun createDropdownMenu(hiddenProviders: List<String>, modelsMap: Map<String, String>, context: Context, aiContainer: LinearLayout): MaterialButton {
return MaterialButton(context, null, R.attr.materialButtonOutlinedStyle).apply {
text = context.getString(R.string.more)
textSize = 18f
cornerRadius = 20
strokeWidth = 2
setPadding(25, 21, 25, 21)
setBackgroundColor(ContextCompat.getColor(context, R.color.disable_color))

layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
setMargins(12, 4, 12, 4)
}

setOnClickListener { view ->
val popupMenu = PopupMenu(context, view)
hiddenProviders.forEach { providerName ->
popupMenu.menu.add(providerName.capitalize(Locale.getDefault()))
}

if (index < providersMap.size - 1) {
val divider = View(currentContext).apply {
layoutParams = TableRow.LayoutParams(1, TableRow.LayoutParams.MATCH_PARENT).apply {
setMargins(8, 0, 8, 0)
popupMenu.setOnMenuItemClickListener { menuItem ->
val selectedProvider = menuItem.title.toString().lowercase()
val selectedModel = modelsMap[selectedProvider] ?: ""

if (!isButtonAlreadyAdded(aiContainer, selectedProvider)) {
val button = createStyledAIButton(selectedProvider, selectedModel.lowercase(), context)
aiContainer.addView(button, aiContainer.childCount - 1)
}
setBackgroundColor(ContextCompat.getColor(currentContext, R.color.hint_color))

true
}
aiTableRow.addView(divider)

popupMenu.show()
}
}
}

aiTableRow.getChildAt(0)?.performClick()
private fun isButtonAlreadyAdded(aiContainer: LinearLayout, providerName: String): Boolean {
for (i in 0 until aiContainer.childCount) {
val view = aiContainer.getChildAt(i)
if (view is MaterialButton && view.text.toString().equals(providerName, ignoreCase = true)) {
return true
}
}
return false
}

private fun selectAI(selectedButton: Button, providerName: String, modelName: String) {
val aiTableRow = fragmentChatDetailBinding.aiTableRow
val aiTableRow = fragmentChatDetailBinding.aiContainer
val context = requireContext()

for (i in 0 until aiTableRow.childCount) {
Expand All @@ -297,7 +367,7 @@ class ChatDetailFragment : Fragment() {
}
}

aiName = providerName
aiName = providerName.lowercase()
aiModel = modelName

clearChatDetail()
Expand Down
27 changes: 17 additions & 10 deletions app/src/main/res/layout/fragment_chat_detail.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,26 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TableLayout
android:id="@+id/aiTbLayout"
android:layout_width="wrap_content"

<HorizontalScrollView
android:id="@+id/aiScrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:fillViewport="true"
android:scrollbars="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:layout_marginTop="10dp">

<TableRow
android:id="@+id/aiTableRow"
<LinearLayout
android:id="@+id/aiContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</TableLayout>
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="8dp" />
</HorizontalScrollView>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_gchat"
Expand All @@ -42,7 +49,7 @@
app:layout_constraintBottom_toTopOf="@+id/text_gchat_indicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/aiTbLayout" />
app:layout_constraintTop_toBottomOf="@+id/aiScrollView" />
<TextView
android:id="@+id/text_gchat_indicator"
android:layout_width="wrap_content"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1233,5 +1233,6 @@
<string name="fetching_ai_providers">جلب مزودي الذكاء الاصطناعي…</string>
<string name="adopt_survey">تبنَّى الاستبيان</string>
<string name="survey_adopted_successfully">تم تبني الاستبيان بنجاح!</string>
<string name="more">المزيد</string>

</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1233,5 +1233,6 @@
<string name="fetching_ai_providers">Obteniendo proveedores de IA…</string>
<string name="adopt_survey">Adoptar encuesta</string>
<string name="survey_adopted_successfully">¡Encuesta adoptada con éxito!</string>
<string name="more">más</string>

</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1233,5 +1233,6 @@
<string name="fetching_ai_providers">Récupération des fournisseurs d\'IA…</string>
<string name="adopt_survey">Adopter le sondage</string>
<string name="survey_adopted_successfully">Enquête adoptée avec succès !</string>
<string name="more">plus</string>

</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-ne/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1233,5 +1233,6 @@
<string name="fetching_ai_providers">एआई प्रदायकहरू प्राप्त गर्दै…</string>
<string name="adopt_survey">सर्वेक्षण अपनाउनुहोस्</string>
<string name="survey_adopted_successfully">सर्वेक्षण सफलतापूर्वक अपनाइयो!</string>
<string name="more">थप</string>

</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-so/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1233,5 +1233,6 @@
<string name="fetching_ai_providers">Helitaanka bixiyeyaasha AI…</string>
<string name="adopt_survey">Sahaminta qaado</string>
<string name="survey_adopted_successfully">Sahanka si guul ah ayaa la qaatay!</string>
<string name="more">inbadan</string>

</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1233,5 +1233,6 @@
<string name="fetching_ai_providers">Fetching AI providers…</string>
<string name="adopt_survey">Adopt survey</string>
<string name="survey_adopted_successfully">Survey adopted successfully!</string>
<string name="more">more</string>

</resources>
Loading