Initial commit
							
								
								
									
										15
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,15 @@ | |||
| *.iml | ||||
| .gradle | ||||
| /local.properties | ||||
| /.idea/caches | ||||
| /.idea/libraries | ||||
| /.idea/modules.xml | ||||
| /.idea/workspace.xml | ||||
| /.idea/navEditor.xml | ||||
| /.idea/assetWizardSettings.xml | ||||
| .DS_Store | ||||
| /build | ||||
| /captures | ||||
| .externalNativeBuild | ||||
| .cxx | ||||
| local.properties | ||||
							
								
								
									
										3
									
								
								.idea/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,3 @@ | |||
| # Default ignored files | ||||
| /shelf/ | ||||
| /workspace.xml | ||||
							
								
								
									
										1
									
								
								.idea/.name
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1 @@ | |||
| Chat App | ||||
							
								
								
									
										6
									
								
								.idea/AndroidProjectSystem.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="AndroidProjectSystem"> | ||||
|     <option name="providerId" value="com.android.tools.idea.GradleProjectSystem" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										10
									
								
								.idea/deploymentTargetSelector.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,10 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="deploymentTargetSelector"> | ||||
|     <selectionStates> | ||||
|       <SelectionState runConfigName="app"> | ||||
|         <option name="selectionMode" value="DROPDOWN" /> | ||||
|       </SelectionState> | ||||
|     </selectionStates> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										18
									
								
								.idea/gradle.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,18 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="GradleSettings"> | ||||
|     <option name="linkedExternalProjectsSettings"> | ||||
|       <GradleProjectSettings> | ||||
|         <option name="testRunner" value="CHOOSE_PER_TEST" /> | ||||
|         <option name="externalProjectPath" value="$PROJECT_DIR$" /> | ||||
|         <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" /> | ||||
|         <option name="modules"> | ||||
|           <set> | ||||
|             <option value="$PROJECT_DIR$" /> | ||||
|             <option value="$PROJECT_DIR$/app" /> | ||||
|           </set> | ||||
|         </option> | ||||
|       </GradleProjectSettings> | ||||
|     </option> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								.idea/kotlinc.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="KotlinJpsPluginSettings"> | ||||
|     <option name="version" value="2.1.21" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										10
									
								
								.idea/migrations.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,10 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectMigrations"> | ||||
|     <option name="MigrateToGradleLocalJavaHome"> | ||||
|       <set> | ||||
|         <option value="$PROJECT_DIR$" /> | ||||
|       </set> | ||||
|     </option> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										10
									
								
								.idea/misc.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,10 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ExternalStorageConfigurationManager" enabled="true" /> | ||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> | ||||
|     <output url="file://$PROJECT_DIR$/build/classes" /> | ||||
|   </component> | ||||
|   <component name="ProjectType"> | ||||
|     <option name="id" value="Android" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										17
									
								
								.idea/runConfigurations.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,17 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="RunConfigurationProducerService"> | ||||
|     <option name="ignoredProducers"> | ||||
|       <set> | ||||
|         <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" /> | ||||
|         <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" /> | ||||
|         <option value="com.intellij.execution.junit.PatternConfigurationProducer" /> | ||||
|         <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" /> | ||||
|         <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" /> | ||||
|         <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" /> | ||||
|         <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" /> | ||||
|         <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" /> | ||||
|       </set> | ||||
|     </option> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="$PROJECT_DIR$" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										1
									
								
								app/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1 @@ | |||
| /build | ||||
							
								
								
									
										68
									
								
								app/build.gradle.kts
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,68 @@ | |||
| plugins { | ||||
|     alias(libs.plugins.android.application) | ||||
|     alias(libs.plugins.kotlin.android) | ||||
|     alias(libs.plugins.kotlin.compose) | ||||
|     alias(libs.plugins.hilt.android) | ||||
|     alias(libs.plugins.ksp) | ||||
|     alias(libs.plugins.secrets) | ||||
| } | ||||
| 
 | ||||
| android { | ||||
|     namespace = "eu.m724.chatapp" | ||||
|     compileSdk = 35 | ||||
| 
 | ||||
|     defaultConfig { | ||||
|         applicationId = "eu.m724.chatapp" | ||||
|         minSdk = 35 | ||||
|         targetSdk = 35 | ||||
|         versionCode = 1 | ||||
|         versionName = "1.0" | ||||
| 
 | ||||
|         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||||
|     } | ||||
| 
 | ||||
|     buildTypes { | ||||
|         release { | ||||
|             isMinifyEnabled = false | ||||
|             proguardFiles( | ||||
|                 getDefaultProguardFile("proguard-android-optimize.txt"), | ||||
|                 "proguard-rules.pro" | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|     compileOptions { | ||||
|         sourceCompatibility = JavaVersion.VERSION_11 | ||||
|         targetCompatibility = JavaVersion.VERSION_11 | ||||
|     } | ||||
|     kotlinOptions { | ||||
|         jvmTarget = "11" | ||||
|     } | ||||
|     buildFeatures { | ||||
|         compose = true | ||||
|         buildConfig = true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
|     implementation(libs.androidx.core.ktx) | ||||
|     implementation(libs.androidx.appcompat) | ||||
|     implementation(libs.material) | ||||
|     implementation(libs.androidx.lifecycle.runtime.ktx) | ||||
|     implementation(libs.androidx.activity.compose) | ||||
|     implementation(platform(libs.androidx.compose.bom)) | ||||
|     implementation(libs.androidx.ui) | ||||
|     implementation(libs.androidx.ui.graphics) | ||||
|     implementation(libs.androidx.ui.tooling.preview) | ||||
|     implementation(libs.androidx.material3) | ||||
|     implementation(libs.hilt.android) | ||||
|     implementation(libs.retrofit) | ||||
|     implementation(libs.retrofit.converter.gson) | ||||
|     testImplementation(libs.junit) | ||||
|     androidTestImplementation(libs.androidx.junit) | ||||
|     androidTestImplementation(libs.androidx.espresso.core) | ||||
|     androidTestImplementation(platform(libs.androidx.compose.bom)) | ||||
|     androidTestImplementation(libs.androidx.ui.test.junit4) | ||||
|     debugImplementation(libs.androidx.ui.tooling) | ||||
|     debugImplementation(libs.androidx.ui.test.manifest) | ||||
|     ksp(libs.hilt.compiler) | ||||
| } | ||||
							
								
								
									
										21
									
								
								app/proguard-rules.pro
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,21 @@ | |||
| # Add project specific ProGuard rules here. | ||||
| # You can control the set of applied configuration files using the | ||||
| # proguardFiles setting in build.gradle. | ||||
| # | ||||
| # For more details, see | ||||
| #   http://developer.android.com/guide/developing/tools/proguard.html | ||||
| 
 | ||||
| # If your project uses WebView with JS, uncomment the following | ||||
| # and specify the fully qualified class name to the JavaScript interface | ||||
| # class: | ||||
| #-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||||
| #   public *; | ||||
| #} | ||||
| 
 | ||||
| # Uncomment this to preserve the line number information for | ||||
| # debugging stack traces. | ||||
| #-keepattributes SourceFile,LineNumberTable | ||||
| 
 | ||||
| # If you keep the line number information, uncomment this to | ||||
| # hide the original source file name. | ||||
| #-renamesourcefileattribute SourceFile | ||||
|  | @ -0,0 +1,24 @@ | |||
| package eu.m724.chatapp | ||||
| 
 | ||||
| import androidx.test.platform.app.InstrumentationRegistry | ||||
| import androidx.test.ext.junit.runners.AndroidJUnit4 | ||||
| 
 | ||||
| import org.junit.Test | ||||
| import org.junit.runner.RunWith | ||||
| 
 | ||||
| import org.junit.Assert.* | ||||
| 
 | ||||
| /** | ||||
|  * Instrumented test, which will execute on an Android device. | ||||
|  * | ||||
|  * See [testing documentation](http://d.android.com/tools/testing). | ||||
|  */ | ||||
| @RunWith(AndroidJUnit4::class) | ||||
| class ExampleInstrumentedTest { | ||||
|     @Test | ||||
|     fun useAppContext() { | ||||
|         // Context of the app under test. | ||||
|         val appContext = InstrumentationRegistry.getInstrumentation().targetContext | ||||
|         assertEquals("eu.m724.chatapp", appContext.packageName) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										30
									
								
								app/src/main/AndroidManifest.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,30 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
| 
 | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
| 
 | ||||
|     <application | ||||
|         android:name=".ChatApplication" | ||||
|         android:allowBackup="true" | ||||
|         android:dataExtractionRules="@xml/data_extraction_rules" | ||||
|         android:fullBackupContent="@xml/backup_rules" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
|         android:roundIcon="@mipmap/ic_launcher_round" | ||||
|         android:supportsRtl="true" | ||||
|         android:theme="@style/Theme.ChatApp"> | ||||
|         <activity | ||||
|             android:name=".activity.chat.ConversationActivity" | ||||
|             android:exported="true" | ||||
|             android:label="@string/title_activity_chat" | ||||
|             android:theme="@style/Theme.ChatApp"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
| 
 | ||||
|                 <category android:name="android.intent.category.LAUNCHER" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|     </application> | ||||
| 
 | ||||
| </manifest> | ||||
							
								
								
									
										8
									
								
								app/src/main/java/eu/m724/chatapp/ChatApplication.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,8 @@ | |||
| package eu.m724.chatapp | ||||
| 
 | ||||
| import android.app.Application | ||||
| import dagger.hilt.android.HiltAndroidApp | ||||
| 
 | ||||
| @HiltAndroidApp | ||||
| class ChatApplication : Application() { | ||||
| } | ||||
|  | @ -0,0 +1,363 @@ | |||
| package eu.m724.chatapp.activity.chat | ||||
| 
 | ||||
| import android.R.attr.navigationIcon | ||||
| import android.R.attr.singleLine | ||||
| import android.R.attr.text | ||||
| import android.os.Bundle | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
| import androidx.activity.enableEdgeToEdge | ||||
| import androidx.activity.viewModels | ||||
| import androidx.compose.animation.AnimatedVisibility | ||||
| import androidx.compose.animation.animateContentSize | ||||
| import androidx.compose.animation.core.animateFloatAsState | ||||
| import androidx.compose.animation.core.tween | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.PaddingValues | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.defaultMinSize | ||||
| import androidx.compose.foundation.layout.fillMaxHeight | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.requiredWidth | ||||
| import androidx.compose.foundation.layout.size | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.material3.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.lifecycle.compose.collectAsStateWithLifecycle | ||||
| import dagger.hilt.android.AndroidEntryPoint | ||||
| import eu.m724.chatapp.activity.ui.theme.ChatAppTheme | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.foundation.text.BasicTextField | ||||
| import androidx.compose.foundation.text.KeyboardActions | ||||
| import androidx.compose.foundation.text.KeyboardOptions | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.AccountCircle | ||||
| import androidx.compose.material.icons.filled.ArrowBack | ||||
| import androidx.compose.material.icons.filled.Close | ||||
| import androidx.compose.material.icons.filled.KeyboardArrowDown | ||||
| import androidx.compose.material.icons.filled.KeyboardArrowUp | ||||
| import androidx.compose.material.icons.filled.Search | ||||
| import androidx.compose.material3.Card | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.IconButton | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.TextButton | ||||
| import androidx.compose.material3.TextField | ||||
| import androidx.compose.material3.TextFieldDefaults | ||||
| import androidx.compose.material3.TopAppBar | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.draw.rotate | ||||
| import androidx.compose.ui.focus.FocusRequester | ||||
| import androidx.compose.ui.focus.focusRequester | ||||
| import androidx.compose.ui.graphics.Brush | ||||
| import androidx.compose.ui.graphics.Color | ||||
| import androidx.compose.ui.text.font.FontWeight | ||||
| import androidx.compose.ui.text.input.KeyboardType | ||||
| import androidx.compose.ui.text.input.PlatformImeOptions | ||||
| import androidx.compose.ui.text.input.VisualTransformation | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.unit.dp | ||||
| import eu.m724.chatapp.api.response.LanguageModel | ||||
| import java.math.RoundingMode | ||||
| import java.text.DecimalFormat | ||||
| 
 | ||||
| @AndroidEntryPoint | ||||
| class ConversationActivity : ComponentActivity() { | ||||
|     private val viewModel: SelectModelViewModel by viewModels() | ||||
| 
 | ||||
|     @OptIn(ExperimentalMaterial3Api::class) | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
| 
 | ||||
|         viewModel.getModels() | ||||
| 
 | ||||
|         enableEdgeToEdge() | ||||
|         setContent { | ||||
|             ChatAppTheme { | ||||
|                 Scaffold( | ||||
|                     modifier = Modifier.fillMaxSize(), | ||||
|                     topBar = { | ||||
|                         var searching by remember { mutableStateOf(false) } | ||||
|                         var searchQuery by remember { mutableStateOf("") } | ||||
| 
 | ||||
|                         TopAppBar( | ||||
|                             title = { | ||||
|                                 Text( | ||||
|                                     text = "Model picker", | ||||
|                                     maxLines = 1 | ||||
|                                 ) | ||||
|                             }, | ||||
|                             navigationIcon = { | ||||
|                                 // TODO animate this | ||||
|                                 if (!searching) { | ||||
|                                     IconButton( | ||||
|                                         onClick = { | ||||
|                                             // TODO call callback here or whatever | ||||
|                                         } | ||||
|                                     ) { | ||||
|                                         Icon( | ||||
|                                             imageVector = Icons.Default.ArrowBack, | ||||
|                                             contentDescription = "Exit model picker" | ||||
|                                         ) | ||||
|                                     } | ||||
|                                 } else { | ||||
|                                     IconButton( | ||||
|                                         onClick = { | ||||
|                                             searching = false | ||||
|                                         } | ||||
|                                     ) { | ||||
|                                         Icon( | ||||
|                                             imageVector = Icons.Default.Close, | ||||
|                                             contentDescription = "Exit search" | ||||
|                                         ) | ||||
|                                     } | ||||
|                                 } | ||||
|                             }, | ||||
|                             actions = { | ||||
|                                 Row( | ||||
|                                     modifier = Modifier.animateContentSize(), | ||||
|                                     verticalAlignment = Alignment.CenterVertically | ||||
|                                 ) { | ||||
|                                     val focusRequester = remember { FocusRequester() } | ||||
| 
 | ||||
|                                     TextField( | ||||
|                                         value = searchQuery, | ||||
|                                         onValueChange = { | ||||
|                                             searchQuery = it | ||||
|                                         }, // TODO cal here too search or something | ||||
|                                         modifier = Modifier.fillMaxWidth( | ||||
|                                             if (searching) 1f else 0f | ||||
|                                         ).padding(start = 64.dp, end = 64.dp).focusRequester(focusRequester), | ||||
|                                         singleLine = true, | ||||
|                                         placeholder = { | ||||
|                                             Text("Search for a model...") | ||||
|                                         }, | ||||
|                                         colors = TextFieldDefaults.colors( | ||||
|                                             unfocusedContainerColor = Color.Transparent, | ||||
|                                             focusedContainerColor = Color.Transparent, | ||||
|                                             focusedIndicatorColor = Color.Transparent | ||||
|                                         ) | ||||
|                                     ) | ||||
| 
 | ||||
|                                     IconButton( | ||||
|                                         onClick = { | ||||
|                                             searching = !searching | ||||
|                                             if (searching) { | ||||
|                                                 focusRequester.requestFocus() | ||||
|                                             } | ||||
|                                         } | ||||
|                                     ) { | ||||
|                                         Icon( | ||||
|                                             imageVector = Icons.Default.Search, | ||||
|                                             contentDescription = "Search" | ||||
|                                         ) | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         ) | ||||
|                     } | ||||
|                 ) { innerPadding -> | ||||
|                     val uiState by viewModel.uiState.collectAsStateWithLifecycle() | ||||
| 
 | ||||
|                     LazyColumn( | ||||
|                         modifier = Modifier.padding(innerPadding) | ||||
|                     ) { | ||||
|                         items(uiState.models) { model -> | ||||
|                             ModelCard(model) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun ModelCard( | ||||
|     model: LanguageModel, | ||||
|     modifier: Modifier = Modifier, | ||||
|     onSelected: () -> Unit = {}, | ||||
| ) { | ||||
|     var expanded by remember { mutableStateOf(false) } | ||||
| 
 | ||||
|     Card( | ||||
|         modifier = modifier | ||||
|             .fillMaxWidth() | ||||
|             .padding(horizontal = 16.dp, vertical = 8.dp), | ||||
|         onClick = { | ||||
|             expanded = !expanded | ||||
|         } | ||||
|     ) { | ||||
|         Column( | ||||
|             modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 12.dp) | ||||
|         ) { | ||||
|             Row( | ||||
|                 modifier = Modifier.fillMaxWidth() | ||||
|             ) { | ||||
|                 Column { | ||||
|                     Icon( | ||||
|                         imageVector = Icons.Default.AccountCircle, | ||||
|                         contentDescription = "Icon for ${model.name}", | ||||
|                         modifier = Modifier.size(48.dp).fillMaxHeight(), | ||||
|                         tint = MaterialTheme.colorScheme.primary | ||||
|                     ) | ||||
| 
 | ||||
|                     // TODO this button is awkward | ||||
|                     AnimatedVisibility(expanded) { | ||||
|                         TextButton( | ||||
|                             onClick = onSelected, | ||||
|                             contentPadding = PaddingValues(horizontal = 0.dp), | ||||
|                             modifier = Modifier.defaultMinSize(minWidth = 1.dp, minHeight = 1.dp).width(48.dp) | ||||
|                         ) { | ||||
|                             Text( | ||||
|                                 text = "Select" | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 Spacer(modifier = Modifier.width(16.dp)) | ||||
| 
 | ||||
|                 Column( | ||||
|                     modifier = Modifier.weight(1f) | ||||
|                 ) { | ||||
|                     Row( | ||||
|                         modifier = Modifier.fillMaxWidth(), | ||||
|                         horizontalArrangement = Arrangement.SpaceBetween | ||||
|                     ) { | ||||
|                         Text( | ||||
|                             text = model.name, | ||||
|                             style = MaterialTheme.typography.titleLarge, | ||||
|                         ) | ||||
| 
 | ||||
|                         ExpandArrowIcon( | ||||
|                             expanded = expanded | ||||
|                         ) | ||||
|                     } | ||||
| 
 | ||||
|                     if (model.description != null) { | ||||
|                         Spacer(modifier = Modifier.height(4.dp)) | ||||
|                         Text( | ||||
|                             text = model.description, | ||||
|                             modifier = Modifier.animateContentSize(), | ||||
|                             style = MaterialTheme.typography.bodyMedium, | ||||
|                             color = MaterialTheme.colorScheme.onSurfaceVariant, | ||||
|                             overflow = TextOverflow.Ellipsis, | ||||
|                             maxLines = if (expanded) Int.MAX_VALUE else 1 | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             Spacer(modifier = Modifier.height(12.dp)) | ||||
| 
 | ||||
|             Row( | ||||
|                 modifier = Modifier.fillMaxWidth(), | ||||
|                 verticalAlignment = Alignment.CenterVertically | ||||
|             ) { | ||||
|                 PriceItem( | ||||
|                     icon = Icons.Default.KeyboardArrowUp, | ||||
|                     label = "Input:", | ||||
|                     price = model.pricing.pricePerMillionInputTokens, | ||||
|                     contentDescription = "Price per million input tokens" | ||||
|                 ) | ||||
| 
 | ||||
|                 Spacer(modifier = Modifier.width(12.dp)) | ||||
| 
 | ||||
|                 PriceItem( | ||||
|                     icon = Icons.Default.KeyboardArrowDown, | ||||
|                     label = "Output:", | ||||
|                     price = model.pricing.pricePerMillionOutputTokens, | ||||
|                     contentDescription = "Price per million output tokens" | ||||
|                 ) | ||||
| 
 | ||||
|                 Spacer(modifier = Modifier.weight(1f)) | ||||
| 
 | ||||
|                 Text( | ||||
|                     text = "/ 1M tokens", | ||||
|                     style = MaterialTheme.typography.labelMedium, | ||||
|                     color = MaterialTheme.colorScheme.onSurfaceVariant | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * A small, horizontal composable for displaying a single price point. | ||||
|  */ | ||||
| @Composable | ||||
| private fun PriceItem( | ||||
|     icon: androidx.compose.ui.graphics.vector.ImageVector, | ||||
|     label: String, | ||||
|     price: Double, | ||||
|     contentDescription: String, | ||||
|     modifier: Modifier = Modifier | ||||
| ) { | ||||
|     Row( | ||||
|         modifier = modifier, | ||||
|         verticalAlignment = Alignment.CenterVertically | ||||
|     ) { | ||||
|         Icon( | ||||
|             imageVector = icon, | ||||
|             contentDescription = contentDescription, | ||||
|             // Smaller icon for a more compact look | ||||
|             modifier = Modifier.size(16.dp), | ||||
|             tint = MaterialTheme.colorScheme.onSurfaceVariant | ||||
|         ) | ||||
|         Spacer(modifier = Modifier.width(4.dp)) | ||||
|         Text( | ||||
|             text = label, | ||||
|             style = MaterialTheme.typography.bodySmall, | ||||
|             color = MaterialTheme.colorScheme.onSurfaceVariant | ||||
|         ) | ||||
|         Spacer(modifier = Modifier.width(4.dp)) | ||||
|         Text( | ||||
|             text = "$${roundToAtMost(price, 3)}", | ||||
|             style = MaterialTheme.typography.bodyMedium, | ||||
|             fontWeight = FontWeight.SemiBold | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| private fun ExpandArrowIcon( | ||||
|     expanded: Boolean | ||||
| ) { | ||||
|     val rotation by animateFloatAsState( | ||||
|         targetValue = if (expanded) -180f else 0f, | ||||
|         animationSpec = tween(), | ||||
|         label = "Arrow rotation animation" | ||||
|     ) | ||||
| 
 | ||||
|     Icon( | ||||
|         imageVector = Icons.Default.KeyboardArrowDown, | ||||
|         contentDescription = "Toggle details", | ||||
|         modifier = Modifier.size(16.dp).rotate(rotation), | ||||
|         tint = MaterialTheme.colorScheme.onSurfaceVariant, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| fun roundToAtMost(number: Double, n: Int): String { | ||||
|     // Create a pattern with 'n' optional decimal places | ||||
|     // For n=3, pattern is "#.###" | ||||
|     // For n=0, pattern is "#" | ||||
|     val pattern = if (n > 0) "#." + "#".repeat(n) else "#" | ||||
| 
 | ||||
|     val df = DecimalFormat(pattern) | ||||
|     df.roundingMode = RoundingMode.HALF_UP // Or choose another rounding mode | ||||
|     return df.format(number) | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| package eu.m724.chatapp.activity.chat | ||||
| 
 | ||||
| import eu.m724.chatapp.api.response.LanguageModel | ||||
| 
 | ||||
| data class SelectModelUiState( | ||||
|     val models: List<LanguageModel> = listOf() | ||||
| ) | ||||
|  | @ -0,0 +1,42 @@ | |||
| package eu.m724.chatapp.activity.chat | ||||
| 
 | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
| import eu.m724.chatapp.api.AiApiService | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
| import kotlinx.coroutines.flow.asStateFlow | ||||
| import kotlinx.coroutines.flow.update | ||||
| import kotlinx.coroutines.launch | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| @HiltViewModel | ||||
| class SelectModelViewModel @Inject constructor( | ||||
|     private val aiApiService: AiApiService | ||||
| ) : ViewModel() { | ||||
|     private val _uiState = MutableStateFlow(SelectModelUiState()) | ||||
|     val uiState: StateFlow<SelectModelUiState> = _uiState.asStateFlow() | ||||
| 
 | ||||
|     fun getModels() { | ||||
|         viewModelScope.launch { | ||||
|             val models = aiApiService.getModels() | ||||
| 
 | ||||
|             if (!models.isSuccessful) { | ||||
|                 // TODO fail | ||||
|                 return@launch | ||||
|             } | ||||
| 
 | ||||
|             val languageModels = models.body()?.data | ||||
| 
 | ||||
|             if (languageModels == null) { | ||||
|                 // TODO fail too | ||||
|                 return@launch | ||||
|             } | ||||
| 
 | ||||
|             _uiState.update { | ||||
|                 it.copy(models = languageModels) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										11
									
								
								app/src/main/java/eu/m724/chatapp/activity/ui/theme/Color.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,11 @@ | |||
| package eu.m724.chatapp.activity.ui.theme | ||||
| 
 | ||||
| import androidx.compose.ui.graphics.Color | ||||
| 
 | ||||
| val Purple80 = Color(0xFFD0BCFF) | ||||
| val PurpleGrey80 = Color(0xFFCCC2DC) | ||||
| val Pink80 = Color(0xFFEFB8C8) | ||||
| 
 | ||||
| val Purple40 = Color(0xFF6650a4) | ||||
| val PurpleGrey40 = Color(0xFF625b71) | ||||
| val Pink40 = Color(0xFF7D5260) | ||||
							
								
								
									
										57
									
								
								app/src/main/java/eu/m724/chatapp/activity/ui/theme/Theme.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,57 @@ | |||
| package eu.m724.chatapp.activity.ui.theme | ||||
| 
 | ||||
| import android.os.Build | ||||
| import androidx.compose.foundation.isSystemInDarkTheme | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.darkColorScheme | ||||
| import androidx.compose.material3.dynamicDarkColorScheme | ||||
| import androidx.compose.material3.dynamicLightColorScheme | ||||
| import androidx.compose.material3.lightColorScheme | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| 
 | ||||
| private val DarkColorScheme = darkColorScheme( | ||||
|     primary = Purple80, | ||||
|     secondary = PurpleGrey80, | ||||
|     tertiary = Pink80 | ||||
| ) | ||||
| 
 | ||||
| private val LightColorScheme = lightColorScheme( | ||||
|     primary = Purple40, | ||||
|     secondary = PurpleGrey40, | ||||
|     tertiary = Pink40 | ||||
| 
 | ||||
|     /* Other default colors to override | ||||
|     background = Color(0xFFFFFBFE), | ||||
|     surface = Color(0xFFFFFBFE), | ||||
|     onPrimary = Color.White, | ||||
|     onSecondary = Color.White, | ||||
|     onTertiary = Color.White, | ||||
|     onBackground = Color(0xFF1C1B1F), | ||||
|     onSurface = Color(0xFF1C1B1F), | ||||
|     */ | ||||
| ) | ||||
| 
 | ||||
| @Composable | ||||
| fun ChatAppTheme( | ||||
|     darkTheme: Boolean = isSystemInDarkTheme(), | ||||
|     // Dynamic color is available on Android 12+ | ||||
|     dynamicColor: Boolean = true, | ||||
|     content: @Composable () -> Unit | ||||
| ) { | ||||
|     val colorScheme = when { | ||||
|         dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { | ||||
|             val context = LocalContext.current | ||||
|             if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) | ||||
|         } | ||||
| 
 | ||||
|         darkTheme -> DarkColorScheme | ||||
|         else -> LightColorScheme | ||||
|     } | ||||
| 
 | ||||
|     MaterialTheme( | ||||
|         colorScheme = colorScheme, | ||||
|         typography = Typography, | ||||
|         content = content | ||||
|     ) | ||||
| } | ||||
							
								
								
									
										34
									
								
								app/src/main/java/eu/m724/chatapp/activity/ui/theme/Type.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,34 @@ | |||
| package eu.m724.chatapp.activity.ui.theme | ||||
| 
 | ||||
| import androidx.compose.material3.Typography | ||||
| import androidx.compose.ui.text.TextStyle | ||||
| import androidx.compose.ui.text.font.FontFamily | ||||
| import androidx.compose.ui.text.font.FontWeight | ||||
| import androidx.compose.ui.unit.sp | ||||
| 
 | ||||
| // Set of Material typography styles to start with | ||||
| val Typography = Typography( | ||||
|     bodyLarge = TextStyle( | ||||
|         fontFamily = FontFamily.Default, | ||||
|         fontWeight = FontWeight.Normal, | ||||
|         fontSize = 16.sp, | ||||
|         lineHeight = 24.sp, | ||||
|         letterSpacing = 0.5.sp | ||||
|     ) | ||||
|     /* Other default text styles to override | ||||
|     titleLarge = TextStyle( | ||||
|         fontFamily = FontFamily.Default, | ||||
|         fontWeight = FontWeight.Normal, | ||||
|         fontSize = 22.sp, | ||||
|         lineHeight = 28.sp, | ||||
|         letterSpacing = 0.sp | ||||
|     ), | ||||
|     labelSmall = TextStyle( | ||||
|         fontFamily = FontFamily.Default, | ||||
|         fontWeight = FontWeight.Medium, | ||||
|         fontSize = 11.sp, | ||||
|         lineHeight = 16.sp, | ||||
|         letterSpacing = 0.5.sp | ||||
|     ) | ||||
|     */ | ||||
| ) | ||||
							
								
								
									
										46
									
								
								app/src/main/java/eu/m724/chatapp/api/AiApiNetworkModule.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,46 @@ | |||
| package eu.m724.chatapp.api | ||||
| 
 | ||||
| import dagger.Module | ||||
| import dagger.Provides | ||||
| import dagger.hilt.InstallIn | ||||
| import dagger.hilt.components.SingletonComponent | ||||
| import eu.m724.chatapp.BuildConfig | ||||
| import okhttp3.OkHttpClient | ||||
| import retrofit2.Retrofit | ||||
| import retrofit2.converter.gson.GsonConverterFactory | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| @Module | ||||
| @InstallIn(SingletonComponent::class) | ||||
| object AiApiNetworkModule { | ||||
|     @Provides | ||||
|     @Singleton | ||||
|     fun provideOkHttpClient(): OkHttpClient { | ||||
|         return OkHttpClient.Builder() | ||||
|             .addInterceptor { | ||||
|                 it.proceed( | ||||
|                     it.request().newBuilder() | ||||
|                         .header("User-Agent", BuildConfig.USER_AGENT) | ||||
|                         .build() | ||||
|                 ) | ||||
|                     // TODO add api key here | ||||
|             } | ||||
|             .build() | ||||
|     } | ||||
| 
 | ||||
|     @Provides | ||||
|     @Singleton | ||||
|     fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { | ||||
|         return Retrofit.Builder() | ||||
|             .baseUrl(BuildConfig.API_ENDPOINT) | ||||
|             .client(okHttpClient) | ||||
|             .addConverterFactory(GsonConverterFactory.create()) | ||||
|             .build() | ||||
|     } | ||||
| 
 | ||||
|     @Provides | ||||
|     @Singleton | ||||
|     fun provideAiApiService(retrofit: Retrofit): AiApiService { | ||||
|         return retrofit.create(AiApiService::class.java) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								app/src/main/java/eu/m724/chatapp/api/AiApiService.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,10 @@ | |||
| package eu.m724.chatapp.api | ||||
| 
 | ||||
| import eu.m724.chatapp.api.response.LanguageModelsResponse | ||||
| import retrofit2.Response | ||||
| import retrofit2.http.GET | ||||
| 
 | ||||
| interface AiApiService { | ||||
|     @GET("models?detailed=true") | ||||
|     suspend fun getModels(): Response<LanguageModelsResponse> | ||||
| } | ||||
|  | @ -0,0 +1,54 @@ | |||
| package eu.m724.chatapp.api.response | ||||
| 
 | ||||
| import com.google.gson.annotations.SerializedName | ||||
| 
 | ||||
| data class LanguageModelsResponse( | ||||
|     val data: List<LanguageModel> | ||||
| ) | ||||
| 
 | ||||
| data class LanguageModel( | ||||
|     /** | ||||
|      * The ID of this model. | ||||
|      */ | ||||
|     val id: String, | ||||
| 
 | ||||
|     /** | ||||
|      * The readable name of this model. | ||||
|      */ | ||||
|     val name: String, | ||||
| 
 | ||||
|     /** | ||||
|      * The description of this model. TODO make it null if it equals model name | ||||
|      */ | ||||
|     val description: String?, | ||||
| 
 | ||||
|     /** | ||||
|      * The maximum amount of tokens this model can handle in one sitting. | ||||
|      */ | ||||
|     @SerializedName("context_length") | ||||
|     val contextLength: Int, | ||||
| 
 | ||||
|     /** | ||||
|      * The pricing of this model | ||||
|      */ | ||||
|     val pricing: LanguageModelPricing | ||||
| ) | ||||
| 
 | ||||
| data class LanguageModelPricing( | ||||
|     /** | ||||
|      * The price per million input tokens | ||||
|      */ | ||||
|     @SerializedName("prompt") | ||||
|     val pricePerMillionInputTokens: Double, | ||||
| 
 | ||||
|     /** | ||||
|      * The price per million output tokens | ||||
|      */ | ||||
|     @SerializedName("completion") | ||||
|     val pricePerMillionOutputTokens: Double, | ||||
| 
 | ||||
|     /** | ||||
|      * Currency, as a code, like USD | ||||
|      */ | ||||
|     val currency: String | ||||
| ) | ||||
							
								
								
									
										170
									
								
								app/src/main/res/drawable/ic_launcher_background.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,170 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="108dp" | ||||
|     android:height="108dp" | ||||
|     android:viewportWidth="108" | ||||
|     android:viewportHeight="108"> | ||||
|     <path | ||||
|         android:fillColor="#3DDC84" | ||||
|         android:pathData="M0,0h108v108h-108z" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M9,0L9,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,0L19,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M29,0L29,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M39,0L39,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M49,0L49,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M59,0L59,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M69,0L69,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M79,0L79,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M89,0L89,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M99,0L99,108" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,9L108,9" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,19L108,19" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,29L108,29" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,39L108,39" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,49L108,49" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,59L108,59" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,69L108,69" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,79L108,79" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,89L108,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,99L108,99" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,29L89,29" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,39L89,39" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,49L89,49" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,59L89,59" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,69L89,69" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,79L89,79" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M29,19L29,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M39,19L39,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M49,19L49,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M59,19L59,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M69,19L69,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M79,19L79,89" | ||||
|         android:strokeWidth="0.8" | ||||
|         android:strokeColor="#33FFFFFF" /> | ||||
| </vector> | ||||
							
								
								
									
										30
									
								
								app/src/main/res/drawable/ic_launcher_foreground.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,30 @@ | |||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:aapt="http://schemas.android.com/aapt" | ||||
|     android:width="108dp" | ||||
|     android:height="108dp" | ||||
|     android:viewportWidth="108" | ||||
|     android:viewportHeight="108"> | ||||
|     <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> | ||||
|         <aapt:attr name="android:fillColor"> | ||||
|             <gradient | ||||
|                 android:endX="85.84757" | ||||
|                 android:endY="92.4963" | ||||
|                 android:startX="42.9492" | ||||
|                 android:startY="49.59793" | ||||
|                 android:type="linear"> | ||||
|                 <item | ||||
|                     android:color="#44000000" | ||||
|                     android:offset="0.0" /> | ||||
|                 <item | ||||
|                     android:color="#00000000" | ||||
|                     android:offset="1.0" /> | ||||
|             </gradient> | ||||
|         </aapt:attr> | ||||
|     </path> | ||||
|     <path | ||||
|         android:fillColor="#FFFFFF" | ||||
|         android:fillType="nonZero" | ||||
|         android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" | ||||
|         android:strokeWidth="1" | ||||
|         android:strokeColor="#00000000" /> | ||||
| </vector> | ||||
							
								
								
									
										6
									
								
								app/src/main/res/mipmap-anydpi/ic_launcher.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <background android:drawable="@drawable/ic_launcher_background" /> | ||||
|     <foreground android:drawable="@drawable/ic_launcher_foreground" /> | ||||
|     <monochrome android:drawable="@drawable/ic_launcher_foreground" /> | ||||
| </adaptive-icon> | ||||
							
								
								
									
										6
									
								
								app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <background android:drawable="@drawable/ic_launcher_background" /> | ||||
|     <foreground android:drawable="@drawable/ic_launcher_foreground" /> | ||||
|     <monochrome android:drawable="@drawable/ic_launcher_foreground" /> | ||||
| </adaptive-icon> | ||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 982 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.6 KiB | 
							
								
								
									
										16
									
								
								app/src/main/res/values-night/themes.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,16 @@ | |||
| <resources xmlns:tools="http://schemas.android.com/tools"> | ||||
|     <!-- Base application theme. --> | ||||
|     <style name="Theme.ChatApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> | ||||
|         <!-- Primary brand color. --> | ||||
|         <item name="colorPrimary">@color/purple_200</item> | ||||
|         <item name="colorPrimaryVariant">@color/purple_700</item> | ||||
|         <item name="colorOnPrimary">@color/black</item> | ||||
|         <!-- Secondary brand color. --> | ||||
|         <item name="colorSecondary">@color/teal_200</item> | ||||
|         <item name="colorSecondaryVariant">@color/teal_200</item> | ||||
|         <item name="colorOnSecondary">@color/black</item> | ||||
|         <!-- Status bar color. --> | ||||
|         <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> | ||||
|         <!-- Customize your theme here. --> | ||||
|     </style> | ||||
| </resources> | ||||
							
								
								
									
										10
									
								
								app/src/main/res/values/colors.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,10 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <color name="purple_200">#FFBB86FC</color> | ||||
|     <color name="purple_500">#FF6200EE</color> | ||||
|     <color name="purple_700">#FF3700B3</color> | ||||
|     <color name="teal_200">#FF03DAC5</color> | ||||
|     <color name="teal_700">#FF018786</color> | ||||
|     <color name="black">#FF000000</color> | ||||
|     <color name="white">#FFFFFFFF</color> | ||||
| </resources> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/values/strings.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,5 @@ | |||
| <resources> | ||||
|     <string name="app_name">Chat App</string> | ||||
|     <string name="title_activity_main">MainActivity</string> | ||||
|     <string name="title_activity_chat">ChatActivity</string> | ||||
| </resources> | ||||
							
								
								
									
										16
									
								
								app/src/main/res/values/themes.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,16 @@ | |||
| <resources xmlns:tools="http://schemas.android.com/tools"> | ||||
|     <!-- Base application theme. --> | ||||
|     <style name="Theme.ChatApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> | ||||
|         <!-- Primary brand color. --> | ||||
|         <item name="colorPrimary">@color/purple_500</item> | ||||
|         <item name="colorPrimaryVariant">@color/purple_700</item> | ||||
|         <item name="colorOnPrimary">@color/white</item> | ||||
|         <!-- Secondary brand color. --> | ||||
|         <item name="colorSecondary">@color/teal_200</item> | ||||
|         <item name="colorSecondaryVariant">@color/teal_700</item> | ||||
|         <item name="colorOnSecondary">@color/black</item> | ||||
|         <!-- Status bar color. --> | ||||
|         <item name="android:statusBarColor">?attr/colorPrimaryVariant</item> | ||||
|         <!-- Customize your theme here. --> | ||||
|     </style> | ||||
| </resources> | ||||
							
								
								
									
										13
									
								
								app/src/main/res/xml/backup_rules.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,13 @@ | |||
| <?xml version="1.0" encoding="utf-8"?><!-- | ||||
|    Sample backup rules file; uncomment and customize as necessary. | ||||
|    See https://developer.android.com/guide/topics/data/autobackup | ||||
|    for details. | ||||
|    Note: This file is ignored for devices older than API 31 | ||||
|    See https://developer.android.com/about/versions/12/backup-restore | ||||
| --> | ||||
| <full-backup-content> | ||||
|     <!-- | ||||
|    <include domain="sharedpref" path="."/> | ||||
|    <exclude domain="sharedpref" path="device.xml"/> | ||||
| --> | ||||
| </full-backup-content> | ||||
							
								
								
									
										19
									
								
								app/src/main/res/xml/data_extraction_rules.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,19 @@ | |||
| <?xml version="1.0" encoding="utf-8"?><!-- | ||||
|    Sample data extraction rules file; uncomment and customize as necessary. | ||||
|    See https://developer.android.com/about/versions/12/backup-restore#xml-changes | ||||
|    for details. | ||||
| --> | ||||
| <data-extraction-rules> | ||||
|     <cloud-backup> | ||||
|         <!-- TODO: Use <include> and <exclude> to control what is backed up. | ||||
|         <include .../> | ||||
|         <exclude .../> | ||||
|         --> | ||||
|     </cloud-backup> | ||||
|     <!-- | ||||
|     <device-transfer> | ||||
|         <include .../> | ||||
|         <exclude .../> | ||||
|     </device-transfer> | ||||
|     --> | ||||
| </data-extraction-rules> | ||||
							
								
								
									
										17
									
								
								app/src/test/java/eu/m724/chatapp/ExampleUnitTest.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,17 @@ | |||
| package eu.m724.chatapp | ||||
| 
 | ||||
| import org.junit.Test | ||||
| 
 | ||||
| import org.junit.Assert.* | ||||
| 
 | ||||
| /** | ||||
|  * Example local unit test, which will execute on the development machine (host). | ||||
|  * | ||||
|  * See [testing documentation](http://d.android.com/tools/testing). | ||||
|  */ | ||||
| class ExampleUnitTest { | ||||
|     @Test | ||||
|     fun addition_isCorrect() { | ||||
|         assertEquals(4, 2 + 2) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										9
									
								
								build.gradle.kts
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,9 @@ | |||
| // Top-level build file where you can add configuration options common to all sub-projects/modules. | ||||
| plugins { | ||||
|     alias(libs.plugins.android.application) apply false | ||||
|     alias(libs.plugins.kotlin.android) apply false | ||||
|     alias(libs.plugins.kotlin.compose) apply false | ||||
|     alias(libs.plugins.hilt.android) apply false | ||||
|     alias(libs.plugins.ksp) apply false | ||||
|     alias(libs.plugins.secrets) apply false | ||||
| } | ||||
							
								
								
									
										23
									
								
								gradle.properties
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,23 @@ | |||
| # Project-wide Gradle settings. | ||||
| # IDE (e.g. Android Studio) users: | ||||
| # Gradle settings configured through the IDE *will override* | ||||
| # any settings specified in this file. | ||||
| # For more details on how to configure your build environment visit | ||||
| # http://www.gradle.org/docs/current/userguide/build_environment.html | ||||
| # Specifies the JVM arguments used for the daemon process. | ||||
| # The setting is particularly useful for tweaking memory settings. | ||||
| org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 | ||||
| # When configured, Gradle will run in incubating parallel mode. | ||||
| # This option should only be used with decoupled projects. For more details, visit | ||||
| # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects | ||||
| # org.gradle.parallel=true | ||||
| # AndroidX package structure to make it clearer which packages are bundled with the | ||||
| # Android operating system, and which are packaged with your app's APK | ||||
| # https://developer.android.com/topic/libraries/support-library/androidx-rn | ||||
| android.useAndroidX=true | ||||
| # Kotlin code style for this project: "official" or "obsolete": | ||||
| kotlin.code.style=official | ||||
| # Enables namespacing of each library's R class so that its R class includes only the | ||||
| # resources declared in the library itself and none from the library's dependencies, | ||||
| # thereby reducing the size of the R class for that library | ||||
| android.nonTransitiveRClass=true | ||||
							
								
								
									
										46
									
								
								gradle/libs.versions.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,46 @@ | |||
| [versions] | ||||
| agp = "8.9.3" | ||||
| kotlin = "2.1.21" | ||||
| coreKtx = "1.16.0" | ||||
| junit = "4.13.2" | ||||
| junitVersion = "1.2.1" | ||||
| espressoCore = "3.6.1" | ||||
| appcompat = "1.7.1" | ||||
| material = "1.12.0" | ||||
| lifecycleRuntimeKtx = "2.9.1" | ||||
| activityCompose = "1.10.1" | ||||
| composeBom = "2025.06.00" | ||||
| hilt = "2.56.2" | ||||
| ksp = "2.1.21-2.0.2" | ||||
| retrofit = "3.0.0" | ||||
| secrets = "2.0.1" | ||||
| 
 | ||||
| [libraries] | ||||
| androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } | ||||
| junit = { group = "junit", name = "junit", version.ref = "junit" } | ||||
| androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } | ||||
| androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } | ||||
| androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } | ||||
| material = { group = "com.google.android.material", name = "material", version.ref = "material" } | ||||
| androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } | ||||
| androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } | ||||
| androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } | ||||
| androidx-ui = { group = "androidx.compose.ui", name = "ui" } | ||||
| androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } | ||||
| androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } | ||||
| androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } | ||||
| androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } | ||||
| androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } | ||||
| androidx-material3 = { group = "androidx.compose.material3", name = "material3" } | ||||
| hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } | ||||
| hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } | ||||
| retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" } | ||||
| retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit"} | ||||
| 
 | ||||
| [plugins] | ||||
| android-application = { id = "com.android.application", version.ref = "agp" } | ||||
| kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } | ||||
| kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } | ||||
| hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } | ||||
| ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } | ||||
| secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } | ||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,6 @@ | |||
| #Fri Jun 13 13:15:28 CEST 2025 | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
							
								
								
									
										185
									
								
								gradlew
									
										
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						|  | @ -0,0 +1,185 @@ | |||
| #!/usr/bin/env sh | ||||
| 
 | ||||
| # | ||||
| # Copyright 2015 the original author or authors. | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| # You may obtain a copy of the License at | ||||
| # | ||||
| #      https://www.apache.org/licenses/LICENSE-2.0 | ||||
| # | ||||
| # Unless required by applicable law or agreed to in writing, software | ||||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| # See the License for the specific language governing permissions and | ||||
| # limitations under the License. | ||||
| # | ||||
| 
 | ||||
| ############################################################################## | ||||
| ## | ||||
| ##  Gradle start up script for UN*X | ||||
| ## | ||||
| ############################################################################## | ||||
| 
 | ||||
| # Attempt to set APP_HOME | ||||
| # Resolve links: $0 may be a link | ||||
| PRG="$0" | ||||
| # Need this for relative symlinks. | ||||
| while [ -h "$PRG" ] ; do | ||||
|     ls=`ls -ld "$PRG"` | ||||
|     link=`expr "$ls" : '.*-> \(.*\)$'` | ||||
|     if expr "$link" : '/.*' > /dev/null; then | ||||
|         PRG="$link" | ||||
|     else | ||||
|         PRG=`dirname "$PRG"`"/$link" | ||||
|     fi | ||||
| done | ||||
| SAVED="`pwd`" | ||||
| cd "`dirname \"$PRG\"`/" >/dev/null | ||||
| APP_HOME="`pwd -P`" | ||||
| cd "$SAVED" >/dev/null | ||||
| 
 | ||||
| APP_NAME="Gradle" | ||||
| APP_BASE_NAME=`basename "$0"` | ||||
| 
 | ||||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||
| 
 | ||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||
| MAX_FD="maximum" | ||||
| 
 | ||||
| warn () { | ||||
|     echo "$*" | ||||
| } | ||||
| 
 | ||||
| die () { | ||||
|     echo | ||||
|     echo "$*" | ||||
|     echo | ||||
|     exit 1 | ||||
| } | ||||
| 
 | ||||
| # OS specific support (must be 'true' or 'false'). | ||||
| cygwin=false | ||||
| msys=false | ||||
| darwin=false | ||||
| nonstop=false | ||||
| case "`uname`" in | ||||
|   CYGWIN* ) | ||||
|     cygwin=true | ||||
|     ;; | ||||
|   Darwin* ) | ||||
|     darwin=true | ||||
|     ;; | ||||
|   MINGW* ) | ||||
|     msys=true | ||||
|     ;; | ||||
|   NONSTOP* ) | ||||
|     nonstop=true | ||||
|     ;; | ||||
| esac | ||||
| 
 | ||||
| CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | ||||
| 
 | ||||
| 
 | ||||
| # Determine the Java command to use to start the JVM. | ||||
| if [ -n "$JAVA_HOME" ] ; then | ||||
|     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | ||||
|         # IBM's JDK on AIX uses strange locations for the executables | ||||
|         JAVACMD="$JAVA_HOME/jre/sh/java" | ||||
|     else | ||||
|         JAVACMD="$JAVA_HOME/bin/java" | ||||
|     fi | ||||
|     if [ ! -x "$JAVACMD" ] ; then | ||||
|         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | ||||
| 
 | ||||
| Please set the JAVA_HOME variable in your environment to match the | ||||
| location of your Java installation." | ||||
|     fi | ||||
| else | ||||
|     JAVACMD="java" | ||||
|     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||
| 
 | ||||
| Please set the JAVA_HOME variable in your environment to match the | ||||
| location of your Java installation." | ||||
| fi | ||||
| 
 | ||||
| # Increase the maximum file descriptors if we can. | ||||
| if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then | ||||
|     MAX_FD_LIMIT=`ulimit -H -n` | ||||
|     if [ $? -eq 0 ] ; then | ||||
|         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then | ||||
|             MAX_FD="$MAX_FD_LIMIT" | ||||
|         fi | ||||
|         ulimit -n $MAX_FD | ||||
|         if [ $? -ne 0 ] ; then | ||||
|             warn "Could not set maximum file descriptor limit: $MAX_FD" | ||||
|         fi | ||||
|     else | ||||
|         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" | ||||
|     fi | ||||
| fi | ||||
| 
 | ||||
| # For Darwin, add options to specify how the application appears in the dock | ||||
| if $darwin; then | ||||
|     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" | ||||
| fi | ||||
| 
 | ||||
| # For Cygwin or MSYS, switch paths to Windows format before running java | ||||
| if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then | ||||
|     APP_HOME=`cygpath --path --mixed "$APP_HOME"` | ||||
|     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` | ||||
| 
 | ||||
|     JAVACMD=`cygpath --unix "$JAVACMD"` | ||||
| 
 | ||||
|     # We build the pattern for arguments to be converted via cygpath | ||||
|     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | ||||
|     SEP="" | ||||
|     for dir in $ROOTDIRSRAW ; do | ||||
|         ROOTDIRS="$ROOTDIRS$SEP$dir" | ||||
|         SEP="|" | ||||
|     done | ||||
|     OURCYGPATTERN="(^($ROOTDIRS))" | ||||
|     # Add a user-defined pattern to the cygpath arguments | ||||
|     if [ "$GRADLE_CYGPATTERN" != "" ] ; then | ||||
|         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" | ||||
|     fi | ||||
|     # Now convert the arguments - kludge to limit ourselves to /bin/sh | ||||
|     i=0 | ||||
|     for arg in "$@" ; do | ||||
|         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` | ||||
|         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option | ||||
| 
 | ||||
|         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition | ||||
|             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` | ||||
|         else | ||||
|             eval `echo args$i`="\"$arg\"" | ||||
|         fi | ||||
|         i=`expr $i + 1` | ||||
|     done | ||||
|     case $i in | ||||
|         0) set -- ;; | ||||
|         1) set -- "$args0" ;; | ||||
|         2) set -- "$args0" "$args1" ;; | ||||
|         3) set -- "$args0" "$args1" "$args2" ;; | ||||
|         4) set -- "$args0" "$args1" "$args2" "$args3" ;; | ||||
|         5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; | ||||
|         6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; | ||||
|         7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; | ||||
|         8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; | ||||
|         9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; | ||||
|     esac | ||||
| fi | ||||
| 
 | ||||
| # Escape application args | ||||
| save () { | ||||
|     for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done | ||||
|     echo " " | ||||
| } | ||||
| APP_ARGS=`save "$@"` | ||||
| 
 | ||||
| # Collect all arguments for the java command, following the shell quoting and substitution rules | ||||
| eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" | ||||
| 
 | ||||
| exec "$JAVACMD" "$@" | ||||
							
								
								
									
										89
									
								
								gradlew.bat
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,89 @@ | |||
| @rem | ||||
| @rem Copyright 2015 the original author or authors. | ||||
| @rem | ||||
| @rem Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| @rem you may not use this file except in compliance with the License. | ||||
| @rem You may obtain a copy of the License at | ||||
| @rem | ||||
| @rem      https://www.apache.org/licenses/LICENSE-2.0 | ||||
| @rem | ||||
| @rem Unless required by applicable law or agreed to in writing, software | ||||
| @rem distributed under the License is distributed on an "AS IS" BASIS, | ||||
| @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| @rem See the License for the specific language governing permissions and | ||||
| @rem limitations under the License. | ||||
| @rem | ||||
| 
 | ||||
| @if "%DEBUG%" == "" @echo off | ||||
| @rem ########################################################################## | ||||
| @rem | ||||
| @rem  Gradle startup script for Windows | ||||
| @rem | ||||
| @rem ########################################################################## | ||||
| 
 | ||||
| @rem Set local scope for the variables with windows NT shell | ||||
| if "%OS%"=="Windows_NT" setlocal | ||||
| 
 | ||||
| set DIRNAME=%~dp0 | ||||
| if "%DIRNAME%" == "" set DIRNAME=. | ||||
| set APP_BASE_NAME=%~n0 | ||||
| set APP_HOME=%DIRNAME% | ||||
| 
 | ||||
| @rem Resolve any "." and ".." in APP_HOME to make it shorter. | ||||
| for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi | ||||
| 
 | ||||
| @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | ||||
| 
 | ||||
| @rem Find java.exe | ||||
| if defined JAVA_HOME goto findJavaFromJavaHome | ||||
| 
 | ||||
| set JAVA_EXE=java.exe | ||||
| %JAVA_EXE% -version >NUL 2>&1 | ||||
| if "%ERRORLEVEL%" == "0" goto execute | ||||
| 
 | ||||
| echo. | ||||
| echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||
| echo. | ||||
| echo Please set the JAVA_HOME variable in your environment to match the | ||||
| echo location of your Java installation. | ||||
| 
 | ||||
| goto fail | ||||
| 
 | ||||
| :findJavaFromJavaHome | ||||
| set JAVA_HOME=%JAVA_HOME:"=% | ||||
| set JAVA_EXE=%JAVA_HOME%/bin/java.exe | ||||
| 
 | ||||
| if exist "%JAVA_EXE%" goto execute | ||||
| 
 | ||||
| echo. | ||||
| echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | ||||
| echo. | ||||
| echo Please set the JAVA_HOME variable in your environment to match the | ||||
| echo location of your Java installation. | ||||
| 
 | ||||
| goto fail | ||||
| 
 | ||||
| :execute | ||||
| @rem Setup the command line | ||||
| 
 | ||||
| set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | ||||
| 
 | ||||
| 
 | ||||
| @rem Execute Gradle | ||||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* | ||||
| 
 | ||||
| :end | ||||
| @rem End local scope for the variables with windows NT shell | ||||
| if "%ERRORLEVEL%"=="0" goto mainEnd | ||||
| 
 | ||||
| :fail | ||||
| rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | ||||
| rem the _cmd.exe /c_ return code! | ||||
| if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 | ||||
| exit /b 1 | ||||
| 
 | ||||
| :mainEnd | ||||
| if "%OS%"=="Windows_NT" endlocal | ||||
| 
 | ||||
| :omega | ||||
							
								
								
									
										24
									
								
								settings.gradle.kts
									
										
									
									
									
										Normal file
									
								
							
							
						
						|  | @ -0,0 +1,24 @@ | |||
| pluginManagement { | ||||
|     repositories { | ||||
|         google { | ||||
|             content { | ||||
|                 includeGroupByRegex("com\\.android.*") | ||||
|                 includeGroupByRegex("com\\.google.*") | ||||
|                 includeGroupByRegex("androidx.*") | ||||
|             } | ||||
|         } | ||||
|         mavenCentral() | ||||
|         gradlePluginPortal() | ||||
|     } | ||||
| } | ||||
| dependencyResolutionManagement { | ||||
|     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) | ||||
|     repositories { | ||||
|         google() | ||||
|         mavenCentral() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| rootProject.name = "Chat App" | ||||
| include(":app") | ||||
|   | ||||
 Minecon724
				Minecon724