Skip to content

Commit bb106ad

Browse files
Merge pull request #2 from CodingWithTashi/feat/release-jul
release july added
2 parents d06f6fa + 702a38b commit bb106ad

37 files changed

Lines changed: 865 additions & 58 deletions

.idea/deploymentTargetSelector.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ android {
1212
applicationId "com.kharagedition.tibetankeyboard"
1313
minSdkVersion 21
1414
targetSdkVersion 35
15-
versionCode 11
16-
versionName "1.2.12"
15+
versionCode 12
16+
versionName "1.3.13"
1717

1818
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1919
multiDexEnabled true
@@ -42,7 +42,7 @@ android {
4242
}
4343
configurations {
4444
configureEach {
45-
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
45+
//exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
4646
}
4747
}
4848
}
@@ -54,6 +54,7 @@ dependencies {
5454
implementation 'com.google.android.material:material:1.12.0'
5555
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
5656
implementation 'androidx.preference:preference-ktx:1.2.1'
57+
implementation 'androidx.activity:activity:1.9.2'
5758
testImplementation 'junit:junit:4.13.2'
5859
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
5960
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
@@ -67,4 +68,10 @@ dependencies {
6768
implementation 'com.github.bumptech.glide:glide:4.15.1'
6869
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
6970
implementation 'androidx.work:work-runtime:2.10.0'
71+
72+
implementation "com.google.ai.client.generativeai:generativeai:0.9.0"
73+
74+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
75+
implementation 'com.airbnb.android:lottie:6.6.2'
76+
7077
}

app/src/main/AndroidManifest.xml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<uses-permission android:name="android.permission.INTERNET" />
77
<uses-permission android:name="android.permission.VIBRATE" />
8-
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
8+
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
99

1010
<application
1111
android:name=".TibetanKeyboardApp"
@@ -16,17 +16,21 @@
1616
android:roundIcon="@drawable/icon_launcher"
1717
android:supportsRtl="true"
1818
android:theme="@style/Theme.TibetanKeyboard">
19+
<activity
20+
android:name=".ui.ChatActivity"
21+
android:exported="false"
22+
android:windowSoftInputMode="adjustResize"/>
1923
<activity
2024
android:name=".SettingsActivity"
2125
android:exported="true"
2226
android:label="@string/title_activity_settings"
23-
android:parentActivityName=".ui.HomeActivity"/>
27+
android:parentActivityName=".ui.HomeActivity" />
2428

2529
<service
2630
android:name=".TibetanKeyboard"
31+
android:exported="true"
2732
android:label="Tibetan Keyboard"
28-
android:permission="android.permission.BIND_INPUT_METHOD"
29-
android:exported="true">
33+
android:permission="android.permission.BIND_INPUT_METHOD">
3034
<meta-data
3135
android:name="android.view.im"
3236
android:resource="@xml/method" />
@@ -36,7 +40,8 @@
3640
</intent-filter>
3741
</service>
3842

39-
<activity android:name=".ui.SplashScreenActivity"
43+
<activity
44+
android:name=".ui.SplashScreenActivity"
4045
android:exported="true">
4146
<intent-filter>
4247
<action android:name="android.intent.action.MAIN" />
@@ -51,11 +56,11 @@
5156
<meta-data
5257
android:name="com.google.android.gms.ads.APPLICATION_ID"
5358
android:value="ca-app-pub-8284901143739274~5033675263" />
59+
5460
<property
5561
android:name="android.adservices.AD_SERVICES_CONFIG"
5662
android:resource="@xml/gma_ad_services_config"
5763
tools:replace="android:resource" />
58-
5964
</application>
6065

6166
</manifest>

app/src/main/java/com/kharagedition/tibetankeyboard/TibetanKeyboard.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.kharagedition.tibetankeyboard
22

3+
import android.content.ComponentName
34
import android.content.Context
45
import android.content.SharedPreferences
56
import android.graphics.Color
@@ -139,6 +140,15 @@ class TibetanKeyboard : InputMethodService(), OnKeyboardActionListener {
139140
KeyboardType.SYMBOL_EN -> {
140141
keyboardView?.keyboard = Keyboard(this, R.xml.symbol_en)
141142
}
143+
KeyboardType.GEMINI -> {
144+
// navigate to chatactivity page
145+
var chatactivity = "com.kharagedition.tibetankeyboard.ui.ChatActivity"
146+
var intent = packageManager.getLaunchIntentForPackage("com.kharagedition.tibetankeyboard")
147+
if (intent != null) {
148+
intent.component = ComponentName("com.kharagedition.tibetankeyboard", chatactivity)
149+
startActivity(intent)
150+
}
151+
}
142152

143153
else -> {
144154
var code = i.toChar()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package com.kharagedition.tibetankeyboard.chat
2+
import android.content.ClipData
3+
import android.content.Context
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import android.widget.ImageView
8+
import android.widget.TextView
9+
import androidx.recyclerview.widget.DiffUtil
10+
import androidx.recyclerview.widget.ListAdapter
11+
import androidx.recyclerview.widget.RecyclerView
12+
import com.kharagedition.tibetankeyboard.R
13+
import java.text.SimpleDateFormat
14+
import java.util.Locale
15+
import android.content.ClipboardManager
16+
class ChatAdapter : ListAdapter<ChatMessage, RecyclerView.ViewHolder>(ChatDiffCallback()) {
17+
18+
companion object {
19+
private const val VIEW_TYPE_USER = 1
20+
private const val VIEW_TYPE_ASSISTANT = 2
21+
}
22+
23+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
24+
return when (viewType) {
25+
VIEW_TYPE_USER -> {
26+
val view = LayoutInflater.from(parent.context)
27+
.inflate(R.layout.item_chat_user, parent, false)
28+
UserMessageViewHolder(view)
29+
}
30+
VIEW_TYPE_ASSISTANT -> {
31+
val view = LayoutInflater.from(parent.context)
32+
.inflate(R.layout.item_chat_assistant, parent, false)
33+
AssistantMessageViewHolder(view)
34+
}
35+
else -> throw IllegalArgumentException("Invalid view type")
36+
}
37+
}
38+
39+
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
40+
val message = getItem(position)
41+
42+
when (holder) {
43+
is UserMessageViewHolder -> holder.bind(message)
44+
is AssistantMessageViewHolder -> holder.bind(message)
45+
}
46+
}
47+
48+
override fun getItemViewType(position: Int): Int {
49+
return if (getItem(position).isFromUser) VIEW_TYPE_USER else VIEW_TYPE_ASSISTANT
50+
}
51+
52+
class UserMessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
53+
private val textViewMessage: TextView = itemView.findViewById(R.id.textViewUserMessage)
54+
private val textViewTimestamp: TextView = itemView.findViewById(R.id.textViewTimestamp)
55+
private val timeFormat = SimpleDateFormat("h:mm a", Locale.getDefault())
56+
57+
fun bind(message: ChatMessage) {
58+
textViewMessage.setTextIsSelectable(true)
59+
textViewMessage.text = message.message
60+
textViewTimestamp.text = timeFormat.format(message.timestamp)
61+
}
62+
}
63+
64+
class AssistantMessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
65+
private val textViewMessage: TextView = itemView.findViewById(R.id.textViewAssistantMessage)
66+
private val textViewTimestamp: TextView = itemView.findViewById(R.id.textViewTimestamp)
67+
private val timeFormat = SimpleDateFormat("h:mm a", Locale.getDefault())
68+
private val clipboardImg : ImageView = itemView.findViewById(R.id.clipboardIcon)
69+
fun bind(message: ChatMessage) {
70+
textViewMessage.setTextIsSelectable(true)
71+
textViewMessage.text = message.message
72+
textViewTimestamp.text = timeFormat.format(message.timestamp)
73+
clipboardImg.setOnClickListener {
74+
val clipboard = itemView.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
75+
val clip = ClipData.newPlainText("message", message.message)
76+
clipboard.setPrimaryClip(clip)
77+
78+
79+
}
80+
}
81+
}
82+
83+
class ChatDiffCallback : DiffUtil.ItemCallback<ChatMessage>() {
84+
override fun areItemsTheSame(oldItem: ChatMessage, newItem: ChatMessage): Boolean {
85+
return oldItem.id == newItem.id
86+
}
87+
88+
override fun areContentsTheSame(oldItem: ChatMessage, newItem: ChatMessage): Boolean {
89+
return oldItem == newItem
90+
}
91+
}
92+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.kharagedition.tibetankeyboard.chat
2+
3+
import java.util.Date
4+
5+
data class ChatMessage(
6+
val id: String = System.currentTimeMillis().toString(),
7+
val message: String,
8+
val isFromUser: Boolean,
9+
val timestamp: Date = Date()
10+
)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package com.kharagedition.tibetankeyboard.chat
2+
3+
import com.google.ai.client.generativeai.GenerativeModel
4+
import com.google.ai.client.generativeai.Chat
5+
import com.google.ai.client.generativeai.type.generationConfig
6+
import kotlinx.coroutines.Dispatchers
7+
import kotlinx.coroutines.withContext
8+
9+
class ChatRepository {
10+
private val apiKey = "AIzaSyCxUMaoBVH5SIII7Wa0uQYvjrjI9IjV9cg" // enjoy me expose api key
11+
12+
private val generationConfig = generationConfig {
13+
temperature = 0.7f
14+
topK = 40
15+
topP = 0.95f
16+
maxOutputTokens = 800
17+
}
18+
19+
private val model = GenerativeModel(
20+
modelName = "gemini-2.0-flash",
21+
apiKey = apiKey,
22+
generationConfig = generationConfig
23+
)
24+
25+
// Chat object to maintain conversation history
26+
private var chat: Chat? = null
27+
28+
// Initialize the chat session
29+
suspend fun initializeChat() {
30+
withContext(Dispatchers.IO) {
31+
try {
32+
// Start a new chat session
33+
chat = model.startChat()
34+
35+
// Send system instructions
36+
val systemInstructions = """
37+
Instructions:
38+
- You are master in Tibetan script and language.
39+
- The user will may ask questions in english or tibetan.
40+
- You must respond ONLY in Tibetan script (བོད་ཡིག་).
41+
- Do not respond in any language other than Tibetan script.
42+
"""
43+
44+
// Send the system instructions and get acknowledgment (we'll discard this response)
45+
chat?.sendMessage(systemInstructions)
46+
} catch (e: Exception) {
47+
e.printStackTrace()
48+
}
49+
}
50+
}
51+
52+
suspend fun getResponse(query: String): String {
53+
return withContext(Dispatchers.IO) {
54+
try {
55+
// Initialize chat if it doesn't exist
56+
if (chat == null) {
57+
initializeChat()
58+
}
59+
60+
// Send message and get response with context awareness
61+
val response = chat?.sendMessage(query)
62+
val tibetanResponse = response?.text?.trim() ?: ""
63+
64+
// Fallback in case of empty response
65+
if (tibetanResponse.isBlank()) {
66+
return@withContext "དགོངས་དག། ལན་འདེབས་དཀའ་ངལ་འཕྲད་སོང་། ཡང་བསྐྱར་འབད་བརྩོན་གནང་རོགས།"
67+
}
68+
69+
tibetanResponse
70+
} catch (e: Exception) {
71+
// Log the exception if possible
72+
e.printStackTrace()
73+
"དགོངས་དག། ཕྱི་ཕྱོགས་དང་འབྲེལ་བའི་དཀའ་ངལ་ཞིག་འཕྲད་སོང་། ཡང་བསྐྱར་འབད་བརྩོན་གནང་རོགས།"
74+
}
75+
}
76+
}
77+
78+
// Method to reset the chat session
79+
fun resetChat() {
80+
chat = null
81+
}
82+
}

0 commit comments

Comments
 (0)